Completed
Push — cli ( eb8d78 )
by Andreas
03:57
created

auth.php ➔ auth_deleteprofile()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 23
nc 9
nop 0
dl 0
loc 39
rs 4.909
c 0
b 0
f 0
1
<?php
2
/**
3
 * Authentication library
4
 *
5
 * Including this file will automatically try to login
6
 * a user by calling auth_login()
7
 *
8
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
9
 * @author     Andreas Gohr <[email protected]>
10
 */
11
12
if(!defined('DOKU_INC')) die('meh.');
13
14
// some ACL level defines
15
define('AUTH_NONE', 0);
16
define('AUTH_READ', 1);
17
define('AUTH_EDIT', 2);
18
define('AUTH_CREATE', 4);
19
define('AUTH_UPLOAD', 8);
20
define('AUTH_DELETE', 16);
21
define('AUTH_ADMIN', 255);
22
23
/**
24
 * Initialize the auth system.
25
 *
26
 * This function is automatically called at the end of init.php
27
 *
28
 * This used to be the main() of the auth.php
29
 *
30
 * @todo backend loading maybe should be handled by the class autoloader
31
 * @todo maybe split into multiple functions at the XXX marked positions
32
 * @triggers AUTH_LOGIN_CHECK
33
 * @return bool
34
 */
35
function auth_setup() {
36
    global $conf;
37
    /* @var DokuWiki_Auth_Plugin $auth */
38
    global $auth;
39
    /* @var Input $INPUT */
40
    global $INPUT;
41
    global $AUTH_ACL;
42
    global $lang;
43
    /* @var Doku_Plugin_Controller $plugin_controller */
44
    global $plugin_controller;
45
    $AUTH_ACL = array();
46
47
    if(!$conf['useacl']) return false;
48
49
    // try to load auth backend from plugins
50
    foreach ($plugin_controller->getList('auth') as $plugin) {
51
        if ($conf['authtype'] === $plugin) {
52
            $auth = $plugin_controller->load('auth', $plugin);
53
            break;
54
        }
55
    }
56
57
    if(!isset($auth) || !$auth){
58
        msg($lang['authtempfail'], -1);
59
        return false;
60
    }
61
62
    if ($auth->success == false) {
0 ignored issues
show
Bug introduced by
Accessing success on the interface DokuWiki_PluginInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
63
        // degrade to unauthenticated user
64
        unset($auth);
65
        auth_logoff();
66
        msg($lang['authtempfail'], -1);
67
        return false;
68
    }
69
70
    // do the login either by cookie or provided credentials XXX
71
    $INPUT->set('http_credentials', false);
72
    if(!$conf['rememberme']) $INPUT->set('r', false);
73
74
    // handle renamed HTTP_AUTHORIZATION variable (can happen when a fix like
75
    // the one presented at
76
    // http://www.besthostratings.com/articles/http-auth-php-cgi.html is used
77
    // for enabling HTTP authentication with CGI/SuExec)
78
    if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
79
        $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
80
    // streamline HTTP auth credentials (IIS/rewrite -> mod_php)
81
    if(isset($_SERVER['HTTP_AUTHORIZATION'])) {
82
        list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
83
            explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
84
    }
85
86
    // if no credentials were given try to use HTTP auth (for SSO)
87
    if(!$INPUT->str('u') && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])) {
88
        $INPUT->set('u', $_SERVER['PHP_AUTH_USER']);
89
        $INPUT->set('p', $_SERVER['PHP_AUTH_PW']);
90
        $INPUT->set('http_credentials', true);
91
    }
92
93
    // apply cleaning (auth specific user names, remove control chars)
94
    if (true === $auth->success) {
0 ignored issues
show
Bug introduced by
Accessing success on the interface DokuWiki_PluginInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
95
        $INPUT->set('u', $auth->cleanUser(stripctl($INPUT->str('u'))));
96
        $INPUT->set('p', stripctl($INPUT->str('p')));
97
    }
98
99
    if(!is_null($auth) && $auth->canDo('external')) {
100
        // external trust mechanism in place
101
        $auth->trustExternal($INPUT->str('u'), $INPUT->str('p'), $INPUT->bool('r'));
102
    } else {
103
        $evdata = array(
104
            'user'     => $INPUT->str('u'),
105
            'password' => $INPUT->str('p'),
106
            'sticky'   => $INPUT->bool('r'),
107
            'silent'   => $INPUT->bool('http_credentials')
108
        );
109
        trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
110
    }
111
112
    //load ACL into a global array XXX
113
    $AUTH_ACL = auth_loadACL();
114
115
    return true;
116
}
117
118
/**
119
 * Loads the ACL setup and handle user wildcards
120
 *
121
 * @author Andreas Gohr <[email protected]>
122
 *
123
 * @return array
124
 */
125
function auth_loadACL() {
126
    global $config_cascade;
127
    global $USERINFO;
128
    /* @var Input $INPUT */
129
    global $INPUT;
130
131
    if(!is_readable($config_cascade['acl']['default'])) return array();
132
133
    $acl = file($config_cascade['acl']['default']);
134
135
    $out = array();
136
    foreach($acl as $line) {
137
        $line = trim($line);
138
        if(empty($line) || ($line{0} == '#')) continue; // skip blank lines & comments
139
        list($id,$rest) = preg_split('/[ \t]+/',$line,2);
140
141
        // substitute user wildcard first (its 1:1)
142
        if(strstr($line, '%USER%')){
143
            // if user is not logged in, this ACL line is meaningless - skip it
144
            if (!$INPUT->server->has('REMOTE_USER')) continue;
145
146
            $id   = str_replace('%USER%',cleanID($INPUT->server->str('REMOTE_USER')),$id);
147
            $rest = str_replace('%USER%',auth_nameencode($INPUT->server->str('REMOTE_USER')),$rest);
148
        }
149
150
        // substitute group wildcard (its 1:m)
151
        if(strstr($line, '%GROUP%')){
152
            // if user is not logged in, grps is empty, no output will be added (i.e. skipped)
153
            foreach((array) $USERINFO['grps'] as $grp){
154
                $nid   = str_replace('%GROUP%',cleanID($grp),$id);
155
                $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
156
                $out[] = "$nid\t$nrest";
157
            }
158
        } else {
159
            $out[] = "$id\t$rest";
160
        }
161
    }
162
163
    return $out;
164
}
165
166
/**
167
 * Event hook callback for AUTH_LOGIN_CHECK
168
 *
169
 * @param array $evdata
170
 * @return bool
171
 */
172
function auth_login_wrapper($evdata) {
173
    return auth_login(
174
        $evdata['user'],
175
        $evdata['password'],
176
        $evdata['sticky'],
177
        $evdata['silent']
178
    );
179
}
180
181
/**
182
 * This tries to login the user based on the sent auth credentials
183
 *
184
 * The authentication works like this: if a username was given
185
 * a new login is assumed and user/password are checked. If they
186
 * are correct the password is encrypted with blowfish and stored
187
 * together with the username in a cookie - the same info is stored
188
 * in the session, too. Additonally a browserID is stored in the
189
 * session.
190
 *
191
 * If no username was given the cookie is checked: if the username,
192
 * crypted password and browserID match between session and cookie
193
 * no further testing is done and the user is accepted
194
 *
195
 * If a cookie was found but no session info was availabe the
196
 * blowfish encrypted password from the cookie is decrypted and
197
 * together with username rechecked by calling this function again.
198
 *
199
 * On a successful login $_SERVER[REMOTE_USER] and $USERINFO
200
 * are set.
201
 *
202
 * @author  Andreas Gohr <[email protected]>
203
 *
204
 * @param   string  $user    Username
205
 * @param   string  $pass    Cleartext Password
206
 * @param   bool    $sticky  Cookie should not expire
207
 * @param   bool    $silent  Don't show error on bad auth
208
 * @return  bool             true on successful auth
209
 */
210
function auth_login($user, $pass, $sticky = false, $silent = false) {
211
    global $USERINFO;
212
    global $conf;
213
    global $lang;
214
    /* @var DokuWiki_Auth_Plugin $auth */
215
    global $auth;
216
    /* @var Input $INPUT */
217
    global $INPUT;
218
219
    $sticky ? $sticky = true : $sticky = false; //sanity check
220
221
    if(!$auth) return false;
222
223
    if(!empty($user)) {
224
        //usual login
225
        if(!empty($pass) && $auth->checkPass($user, $pass)) {
226
            // make logininfo globally available
227
            $INPUT->server->set('REMOTE_USER', $user);
228
            $secret                 = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
229
            auth_setCookie($user, auth_encrypt($pass, $secret), $sticky);
230
            return true;
231
        } else {
232
            //invalid credentials - log off
233
            if(!$silent) {
234
                http_status(403, 'Login failed');
235
                msg($lang['badlogin'], -1);
236
            }
237
            auth_logoff();
238
            return false;
239
        }
240
    } else {
241
        // read cookie information
242
        list($user, $sticky, $pass) = auth_getCookie();
243
        if($user && $pass) {
244
            // we got a cookie - see if we can trust it
245
246
            // get session info
247
            $session = $_SESSION[DOKU_COOKIE]['auth'];
248
            if(isset($session) &&
249
                $auth->useSessionCache($user) &&
250
                ($session['time'] >= time() - $conf['auth_security_timeout']) &&
251
                ($session['user'] == $user) &&
252
                ($session['pass'] == sha1($pass)) && //still crypted
253
                ($session['buid'] == auth_browseruid())
254
            ) {
255
256
                // he has session, cookie and browser right - let him in
257
                $INPUT->server->set('REMOTE_USER', $user);
258
                $USERINFO               = $session['info']; //FIXME move all references to session
259
                return true;
260
            }
261
            // no we don't trust it yet - recheck pass but silent
262
            $secret = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
263
            $pass   = auth_decrypt($pass, $secret);
264
            return auth_login($user, $pass, $sticky, true);
265
        }
266
    }
267
    //just to be sure
268
    auth_logoff(true);
269
    return false;
270
}
271
272
/**
273
 * Builds a pseudo UID from browser and IP data
274
 *
275
 * This is neither unique nor unfakable - still it adds some
276
 * security. Using the first part of the IP makes sure
277
 * proxy farms like AOLs are still okay.
278
 *
279
 * @author  Andreas Gohr <[email protected]>
280
 *
281
 * @return  string  a MD5 sum of various browser headers
282
 */
283
function auth_browseruid() {
284
    /* @var Input $INPUT */
285
    global $INPUT;
286
287
    $ip  = clientIP(true);
288
    $uid = '';
289
    $uid .= $INPUT->server->str('HTTP_USER_AGENT');
290
    $uid .= $INPUT->server->str('HTTP_ACCEPT_CHARSET');
291
    $uid .= substr($ip, 0, strpos($ip, '.'));
292
    $uid = strtolower($uid);
293
    return md5($uid);
294
}
295
296
/**
297
 * Creates a random key to encrypt the password in cookies
298
 *
299
 * This function tries to read the password for encrypting
300
 * cookies from $conf['metadir'].'/_htcookiesalt'
301
 * if no such file is found a random key is created and
302
 * and stored in this file.
303
 *
304
 * @author  Andreas Gohr <[email protected]>
305
 *
306
 * @param   bool $addsession if true, the sessionid is added to the salt
307
 * @param   bool $secure     if security is more important than keeping the old value
308
 * @return  string
309
 */
310
function auth_cookiesalt($addsession = false, $secure = false) {
311
    global $conf;
312
    $file = $conf['metadir'].'/_htcookiesalt';
313
    if ($secure || !file_exists($file)) {
314
        $file = $conf['metadir'].'/_htcookiesalt2';
315
    }
316
    $salt = io_readFile($file);
317
    if(empty($salt)) {
318
        $salt = bin2hex(auth_randombytes(64));
319
        io_saveFile($file, $salt);
320
    }
321
    if($addsession) {
322
        $salt .= session_id();
323
    }
324
    return $salt;
325
}
326
327
/**
328
 * Return cryptographically secure random bytes.
329
 *
330
 * @author Niklas Keller <[email protected]>
331
 *
332
 * @param int $length number of bytes
333
 * @return string cryptographically secure random bytes
334
 */
335
function auth_randombytes($length) {
336
    return random_bytes($length);
337
}
338
339
/**
340
 * Cryptographically secure random number generator.
341
 *
342
 * @author Niklas Keller <[email protected]>
343
 *
344
 * @param int $min
345
 * @param int $max
346
 * @return int
347
 */
348
function auth_random($min, $max) {
349
    return random_int($min, $max);
350
}
351
352
/**
353
 * Encrypt data using the given secret using AES
354
 *
355
 * The mode is CBC with a random initialization vector, the key is derived
356
 * using pbkdf2.
357
 *
358
 * @param string $data   The data that shall be encrypted
359
 * @param string $secret The secret/password that shall be used
360
 * @return string The ciphertext
361
 */
362
function auth_encrypt($data, $secret) {
363
    $iv     = auth_randombytes(16);
364
    $cipher = new \phpseclib\Crypt\AES();
365
    $cipher->setPassword($secret);
366
367
    /*
368
    this uses the encrypted IV as IV as suggested in
369
    http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, Appendix C
370
    for unique but necessarily random IVs. The resulting ciphertext is
371
    compatible to ciphertext that was created using a "normal" IV.
372
    */
373
    return $cipher->encrypt($iv.$data);
374
}
375
376
/**
377
 * Decrypt the given AES ciphertext
378
 *
379
 * The mode is CBC, the key is derived using pbkdf2
380
 *
381
 * @param string $ciphertext The encrypted data
382
 * @param string $secret     The secret/password that shall be used
383
 * @return string The decrypted data
384
 */
385
function auth_decrypt($ciphertext, $secret) {
386
    $iv     = substr($ciphertext, 0, 16);
387
    $cipher = new \phpseclib\Crypt\AES();
388
    $cipher->setPassword($secret);
389
    $cipher->setIV($iv);
390
391
    return $cipher->decrypt(substr($ciphertext, 16));
392
}
393
394
/**
395
 * Log out the current user
396
 *
397
 * This clears all authentication data and thus log the user
398
 * off. It also clears session data.
399
 *
400
 * @author  Andreas Gohr <[email protected]>
401
 *
402
 * @param bool $keepbc - when true, the breadcrumb data is not cleared
403
 */
404
function auth_logoff($keepbc = false) {
405
    global $conf;
406
    global $USERINFO;
407
    /* @var DokuWiki_Auth_Plugin $auth */
408
    global $auth;
409
    /* @var Input $INPUT */
410
    global $INPUT;
411
412
    // make sure the session is writable (it usually is)
413
    @session_start();
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
414
415
    if(isset($_SESSION[DOKU_COOKIE]['auth']['user']))
416
        unset($_SESSION[DOKU_COOKIE]['auth']['user']);
417
    if(isset($_SESSION[DOKU_COOKIE]['auth']['pass']))
418
        unset($_SESSION[DOKU_COOKIE]['auth']['pass']);
419
    if(isset($_SESSION[DOKU_COOKIE]['auth']['info']))
420
        unset($_SESSION[DOKU_COOKIE]['auth']['info']);
421
    if(!$keepbc && isset($_SESSION[DOKU_COOKIE]['bc']))
422
        unset($_SESSION[DOKU_COOKIE]['bc']);
423
    $INPUT->server->remove('REMOTE_USER');
424
    $USERINFO = null; //FIXME
425
426
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
427
    setcookie(DOKU_COOKIE, '', time() - 600000, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
428
429
    if($auth) $auth->logOff();
430
}
431
432
/**
433
 * Check if a user is a manager
434
 *
435
 * Should usually be called without any parameters to check the current
436
 * user.
437
 *
438
 * The info is available through $INFO['ismanager'], too
439
 *
440
 * @author Andreas Gohr <[email protected]>
441
 * @see    auth_isadmin
442
 *
443
 * @param  string $user       Username
444
 * @param  array  $groups     List of groups the user is in
445
 * @param  bool   $adminonly  when true checks if user is admin
446
 * @return bool
447
 */
448
function auth_ismanager($user = null, $groups = null, $adminonly = false) {
449
    global $conf;
450
    global $USERINFO;
451
    /* @var DokuWiki_Auth_Plugin $auth */
452
    global $auth;
453
    /* @var Input $INPUT */
454
    global $INPUT;
455
456
457
    if(!$auth) return false;
458
    if(is_null($user)) {
459
        if(!$INPUT->server->has('REMOTE_USER')) {
460
            return false;
461
        } else {
462
            $user = $INPUT->server->str('REMOTE_USER');
463
        }
464
    }
465
    if(is_null($groups)) {
466
        $groups = (array) $USERINFO['grps'];
467
    }
468
469
    // check superuser match
470
    if(auth_isMember($conf['superuser'], $user, $groups)) return true;
471
    if($adminonly) return false;
472
    // check managers
473
    if(auth_isMember($conf['manager'], $user, $groups)) return true;
474
475
    return false;
476
}
477
478
/**
479
 * Check if a user is admin
480
 *
481
 * Alias to auth_ismanager with adminonly=true
482
 *
483
 * The info is available through $INFO['isadmin'], too
484
 *
485
 * @author Andreas Gohr <[email protected]>
486
 * @see auth_ismanager()
487
 *
488
 * @param  string $user       Username
489
 * @param  array  $groups     List of groups the user is in
490
 * @return bool
491
 */
492
function auth_isadmin($user = null, $groups = null) {
493
    return auth_ismanager($user, $groups, true);
494
}
495
496
/**
497
 * Match a user and his groups against a comma separated list of
498
 * users and groups to determine membership status
499
 *
500
 * Note: all input should NOT be nameencoded.
501
 *
502
 * @param string $memberlist commaseparated list of allowed users and groups
503
 * @param string $user       user to match against
504
 * @param array  $groups     groups the user is member of
505
 * @return bool       true for membership acknowledged
506
 */
507
function auth_isMember($memberlist, $user, array $groups) {
508
    /* @var DokuWiki_Auth_Plugin $auth */
509
    global $auth;
510
    if(!$auth) return false;
511
512
    // clean user and groups
513
    if(!$auth->isCaseSensitive()) {
514
        $user   = utf8_strtolower($user);
515
        $groups = array_map('utf8_strtolower', $groups);
516
    }
517
    $user   = $auth->cleanUser($user);
518
    $groups = array_map(array($auth, 'cleanGroup'), $groups);
519
520
    // extract the memberlist
521
    $members = explode(',', $memberlist);
522
    $members = array_map('trim', $members);
523
    $members = array_unique($members);
524
    $members = array_filter($members);
525
526
    // compare cleaned values
527
    foreach($members as $member) {
528
        if($member == '@ALL' ) return true;
529
        if(!$auth->isCaseSensitive()) $member = utf8_strtolower($member);
530
        if($member[0] == '@') {
531
            $member = $auth->cleanGroup(substr($member, 1));
532
            if(in_array($member, $groups)) return true;
533
        } else {
534
            $member = $auth->cleanUser($member);
535
            if($member == $user) return true;
536
        }
537
    }
538
539
    // still here? not a member!
540
    return false;
541
}
542
543
/**
544
 * Convinience function for auth_aclcheck()
545
 *
546
 * This checks the permissions for the current user
547
 *
548
 * @author  Andreas Gohr <[email protected]>
549
 *
550
 * @param  string  $id  page ID (needs to be resolved and cleaned)
551
 * @return int          permission level
552
 */
553
function auth_quickaclcheck($id) {
554
    global $conf;
555
    global $USERINFO;
556
    /* @var Input $INPUT */
557
    global $INPUT;
558
    # if no ACL is used always return upload rights
559
    if(!$conf['useacl']) return AUTH_UPLOAD;
560
    return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), $USERINFO['grps']);
561
}
562
563
/**
564
 * Returns the maximum rights a user has for the given ID or its namespace
565
 *
566
 * @author  Andreas Gohr <[email protected]>
567
 *
568
 * @triggers AUTH_ACL_CHECK
569
 * @param  string       $id     page ID (needs to be resolved and cleaned)
570
 * @param  string       $user   Username
571
 * @param  array|null   $groups Array of groups the user is in
572
 * @return int             permission level
573
 */
574
function auth_aclcheck($id, $user, $groups) {
575
    $data = array(
576
        'id'     => $id,
577
        'user'   => $user,
578
        'groups' => $groups
579
    );
580
581
    return trigger_event('AUTH_ACL_CHECK', $data, 'auth_aclcheck_cb');
582
}
583
584
/**
585
 * default ACL check method
586
 *
587
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
588
 *
589
 * @author  Andreas Gohr <[email protected]>
590
 *
591
 * @param  array $data event data
592
 * @return int   permission level
593
 */
594
function auth_aclcheck_cb($data) {
595
    $id     =& $data['id'];
596
    $user   =& $data['user'];
597
    $groups =& $data['groups'];
598
599
    global $conf;
600
    global $AUTH_ACL;
601
    /* @var DokuWiki_Auth_Plugin $auth */
602
    global $auth;
603
604
    // if no ACL is used always return upload rights
605
    if(!$conf['useacl']) return AUTH_UPLOAD;
606
    if(!$auth) return AUTH_NONE;
607
608
    //make sure groups is an array
609
    if(!is_array($groups)) $groups = array();
610
611
    //if user is superuser or in superusergroup return 255 (acl_admin)
612
    if(auth_isadmin($user, $groups)) {
613
        return AUTH_ADMIN;
614
    }
615
616
    if(!$auth->isCaseSensitive()) {
617
        $user   = utf8_strtolower($user);
618
        $groups = array_map('utf8_strtolower', $groups);
619
    }
620
    $user   = auth_nameencode($auth->cleanUser($user));
621
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
622
623
    //prepend groups with @ and nameencode
624
    foreach($groups as &$group) {
625
        $group = '@'.auth_nameencode($group);
626
    }
627
628
    $ns   = getNS($id);
629
    $perm = -1;
630
631
    //add ALL group
632
    $groups[] = '@ALL';
633
634
    //add User
635
    if($user) $groups[] = $user;
636
637
    //check exact match first
638
    $matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
639
    if(count($matches)) {
640
        foreach($matches as $match) {
641
            $match = preg_replace('/#.*$/', '', $match); //ignore comments
642
            $acl   = preg_split('/[ \t]+/', $match);
643
            if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
644
                $acl[1] = utf8_strtolower($acl[1]);
645
            }
646
            if(!in_array($acl[1], $groups)) {
647
                continue;
648
            }
649
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
650
            if($acl[2] > $perm) {
651
                $perm = $acl[2];
652
            }
653
        }
654
        if($perm > -1) {
655
            //we had a match - return it
656
            return (int) $perm;
657
        }
658
    }
659
660
    //still here? do the namespace checks
661
    if($ns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ns of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
662
        $path = $ns.':*';
663
    } else {
664
        $path = '*'; //root document
665
    }
666
667
    do {
668
        $matches = preg_grep('/^'.preg_quote($path, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
669
        if(count($matches)) {
670
            foreach($matches as $match) {
671
                $match = preg_replace('/#.*$/', '', $match); //ignore comments
672
                $acl   = preg_split('/[ \t]+/', $match);
673
                if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
674
                    $acl[1] = utf8_strtolower($acl[1]);
675
                }
676
                if(!in_array($acl[1], $groups)) {
677
                    continue;
678
                }
679
                if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
680
                if($acl[2] > $perm) {
681
                    $perm = $acl[2];
682
                }
683
            }
684
            //we had a match - return it
685
            if($perm != -1) {
686
                return (int) $perm;
687
            }
688
        }
689
        //get next higher namespace
690
        $ns = getNS($ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($ns) on line 690 can also be of type false; however, getNS() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
691
692
        if($path != '*') {
693
            $path = $ns.':*';
694
            if($path == ':*') $path = '*';
695
        } else {
696
            //we did this already
697
            //looks like there is something wrong with the ACL
698
            //break here
699
            msg('No ACL setup yet! Denying access to everyone.');
700
            return AUTH_NONE;
701
        }
702
    } while(1); //this should never loop endless
703
    return AUTH_NONE;
704
}
705
706
/**
707
 * Encode ASCII special chars
708
 *
709
 * Some auth backends allow special chars in their user and groupnames
710
 * The special chars are encoded with this function. Only ASCII chars
711
 * are encoded UTF-8 multibyte are left as is (different from usual
712
 * urlencoding!).
713
 *
714
 * Decoding can be done with rawurldecode
715
 *
716
 * @author Andreas Gohr <[email protected]>
717
 * @see rawurldecode()
718
 *
719
 * @param string $name
720
 * @param bool $skip_group
721
 * @return string
722
 */
723
function auth_nameencode($name, $skip_group = false) {
724
    global $cache_authname;
725
    $cache =& $cache_authname;
726
    $name  = (string) $name;
727
728
    // never encode wildcard FS#1955
729
    if($name == '%USER%') return $name;
730
    if($name == '%GROUP%') return $name;
731
732
    if(!isset($cache[$name][$skip_group])) {
733
        if($skip_group && $name{0} == '@') {
734
            $cache[$name][$skip_group] = '@'.preg_replace_callback(
735
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
736
                'auth_nameencode_callback', substr($name, 1)
737
            );
738
        } else {
739
            $cache[$name][$skip_group] = preg_replace_callback(
740
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
741
                'auth_nameencode_callback', $name
742
            );
743
        }
744
    }
745
746
    return $cache[$name][$skip_group];
747
}
748
749
/**
750
 * callback encodes the matches
751
 *
752
 * @param array $matches first complete match, next matching subpatterms
753
 * @return string
754
 */
755
function auth_nameencode_callback($matches) {
756
    return '%'.dechex(ord(substr($matches[1],-1)));
757
}
758
759
/**
760
 * Create a pronouncable password
761
 *
762
 * The $foruser variable might be used by plugins to run additional password
763
 * policy checks, but is not used by the default implementation
764
 *
765
 * @author   Andreas Gohr <[email protected]>
766
 * @link     http://www.phpbuilder.com/annotate/message.php3?id=1014451
767
 * @triggers AUTH_PASSWORD_GENERATE
768
 *
769
 * @param  string $foruser username for which the password is generated
770
 * @return string  pronouncable password
771
 */
772
function auth_pwgen($foruser = '') {
773
    $data = array(
774
        'password' => '',
775
        'foruser'  => $foruser
776
    );
777
778
    $evt = new Doku_Event('AUTH_PASSWORD_GENERATE', $data);
779
    if($evt->advise_before(true)) {
780
        $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
781
        $v = 'aeiou'; //vowels
782
        $a = $c.$v; //both
783
        $s = '!$%&?+*~#-_:.;,'; // specials
784
785
        //use thre syllables...
786
        for($i = 0; $i < 3; $i++) {
787
            $data['password'] .= $c[auth_random(0, strlen($c) - 1)];
788
            $data['password'] .= $v[auth_random(0, strlen($v) - 1)];
789
            $data['password'] .= $a[auth_random(0, strlen($a) - 1)];
790
        }
791
        //... and add a nice number and special
792
        $data['password'] .= auth_random(10, 99).$s[auth_random(0, strlen($s) - 1)];
793
    }
794
    $evt->advise_after();
795
796
    return $data['password'];
797
}
798
799
/**
800
 * Sends a password to the given user
801
 *
802
 * @author  Andreas Gohr <[email protected]>
803
 *
804
 * @param string $user Login name of the user
805
 * @param string $password The new password in clear text
806
 * @return bool  true on success
807
 */
808
function auth_sendPassword($user, $password) {
809
    global $lang;
810
    /* @var DokuWiki_Auth_Plugin $auth */
811
    global $auth;
812
    if(!$auth) return false;
813
814
    $user     = $auth->cleanUser($user);
815
    $userinfo = $auth->getUserData($user, $requireGroups = false);
816
817
    if(!$userinfo['mail']) return false;
818
819
    $text = rawLocale('password');
820
    $trep = array(
821
        'FULLNAME' => $userinfo['name'],
822
        'LOGIN'    => $user,
823
        'PASSWORD' => $password
824
    );
825
826
    $mail = new Mailer();
827
    $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
828
    $mail->subject($lang['regpwmail']);
829
    $mail->setBody($text, $trep);
830
    return $mail->send();
831
}
832
833
/**
834
 * Register a new user
835
 *
836
 * This registers a new user - Data is read directly from $_POST
837
 *
838
 * @author  Andreas Gohr <[email protected]>
839
 *
840
 * @return bool  true on success, false on any error
841
 */
842
function register() {
843
    global $lang;
844
    global $conf;
845
    /* @var DokuWiki_Auth_Plugin $auth */
846
    global $auth;
847
    global $INPUT;
848
849
    if(!$INPUT->post->bool('save')) return false;
850
    if(!actionOK('register')) return false;
851
852
    // gather input
853
    $login    = trim($auth->cleanUser($INPUT->post->str('login')));
854
    $fullname = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('fullname')));
855
    $email    = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('email')));
856
    $pass     = $INPUT->post->str('pass');
857
    $passchk  = $INPUT->post->str('passchk');
858
859
    if(empty($login) || empty($fullname) || empty($email)) {
860
        msg($lang['regmissing'], -1);
861
        return false;
862
    }
863
864
    if($conf['autopasswd']) {
865
        $pass = auth_pwgen($login); // automatically generate password
866
    } elseif(empty($pass) || empty($passchk)) {
867
        msg($lang['regmissing'], -1); // complain about missing passwords
868
        return false;
869
    } elseif($pass != $passchk) {
870
        msg($lang['regbadpass'], -1); // complain about misspelled passwords
871
        return false;
872
    }
873
874
    //check mail
875
    if(!mail_isvalid($email)) {
876
        msg($lang['regbadmail'], -1);
877
        return false;
878
    }
879
880
    //okay try to create the user
881
    if(!$auth->triggerUserMod('create', array($login, $pass, $fullname, $email))) {
882
        msg($lang['regfail'], -1);
883
        return false;
884
    }
885
886
    // send notification about the new user
887
    $subscription = new Subscription();
888
    $subscription->send_register($login, $fullname, $email);
889
890
    // are we done?
891
    if(!$conf['autopasswd']) {
892
        msg($lang['regsuccess2'], 1);
893
        return true;
894
    }
895
896
    // autogenerated password? then send password to user
897
    if(auth_sendPassword($login, $pass)) {
898
        msg($lang['regsuccess'], 1);
899
        return true;
900
    } else {
901
        msg($lang['regmailfail'], -1);
902
        return false;
903
    }
904
}
905
906
/**
907
 * Update user profile
908
 *
909
 * @author    Christopher Smith <[email protected]>
910
 */
911
function updateprofile() {
912
    global $conf;
913
    global $lang;
914
    /* @var DokuWiki_Auth_Plugin $auth */
915
    global $auth;
916
    /* @var Input $INPUT */
917
    global $INPUT;
918
919
    if(!$INPUT->post->bool('save')) return false;
920
    if(!checkSecurityToken()) return false;
921
922
    if(!actionOK('profile')) {
923
        msg($lang['profna'], -1);
924
        return false;
925
    }
926
927
    $changes         = array();
928
    $changes['pass'] = $INPUT->post->str('newpass');
929
    $changes['name'] = $INPUT->post->str('fullname');
930
    $changes['mail'] = $INPUT->post->str('email');
931
932
    // check misspelled passwords
933
    if($changes['pass'] != $INPUT->post->str('passchk')) {
934
        msg($lang['regbadpass'], -1);
935
        return false;
936
    }
937
938
    // clean fullname and email
939
    $changes['name'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['name']));
940
    $changes['mail'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['mail']));
941
942
    // no empty name and email (except the backend doesn't support them)
943
    if((empty($changes['name']) && $auth->canDo('modName')) ||
944
        (empty($changes['mail']) && $auth->canDo('modMail'))
945
    ) {
946
        msg($lang['profnoempty'], -1);
947
        return false;
948
    }
949
    if(!mail_isvalid($changes['mail']) && $auth->canDo('modMail')) {
950
        msg($lang['regbadmail'], -1);
951
        return false;
952
    }
953
954
    $changes = array_filter($changes);
955
956
    // check for unavailable capabilities
957
    if(!$auth->canDo('modName')) unset($changes['name']);
958
    if(!$auth->canDo('modMail')) unset($changes['mail']);
959
    if(!$auth->canDo('modPass')) unset($changes['pass']);
960
961
    // anything to do?
962
    if(!count($changes)) {
963
        msg($lang['profnochange'], -1);
964
        return false;
965
    }
966
967
    if($conf['profileconfirm']) {
968
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
969
            msg($lang['badpassconfirm'], -1);
970
            return false;
971
        }
972
    }
973
974
    if(!$auth->triggerUserMod('modify', array($INPUT->server->str('REMOTE_USER'), &$changes))) {
975
        msg($lang['proffail'], -1);
976
        return false;
977
    }
978
979
    if($changes['pass']) {
980
        // update cookie and session with the changed data
981
        list( /*user*/, $sticky, /*pass*/) = auth_getCookie();
982
        $pass = auth_encrypt($changes['pass'], auth_cookiesalt(!$sticky, true));
0 ignored issues
show
Bug introduced by
It seems like auth_cookiesalt(!$sticky, true) targeting auth_cookiesalt() can also be of type boolean; however, auth_encrypt() 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...
983
        auth_setCookie($INPUT->server->str('REMOTE_USER'), $pass, (bool) $sticky);
984
    } else {
985
        // make sure the session is writable
986
        @session_start();
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
987
        // invalidate session cache
988
        $_SESSION[DOKU_COOKIE]['auth']['time'] = 0;
989
        session_write_close();
990
    }
991
992
    return true;
993
}
994
995
/**
996
 * Delete the current logged-in user
997
 *
998
 * @return bool true on success, false on any error
999
 */
1000
function auth_deleteprofile(){
1001
    global $conf;
1002
    global $lang;
1003
    /* @var DokuWiki_Auth_Plugin $auth */
1004
    global $auth;
1005
    /* @var Input $INPUT */
1006
    global $INPUT;
1007
1008
    if(!$INPUT->post->bool('delete')) return false;
1009
    if(!checkSecurityToken()) return false;
1010
1011
    // action prevented or auth module disallows
1012
    if(!actionOK('profile_delete') || !$auth->canDo('delUser')) {
1013
        msg($lang['profnodelete'], -1);
1014
        return false;
1015
    }
1016
1017
    if(!$INPUT->post->bool('confirm_delete')){
1018
        msg($lang['profconfdeletemissing'], -1);
1019
        return false;
1020
    }
1021
1022
    if($conf['profileconfirm']) {
1023
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1024
            msg($lang['badpassconfirm'], -1);
1025
            return false;
1026
        }
1027
    }
1028
1029
    $deleted = array();
1030
    $deleted[] = $INPUT->server->str('REMOTE_USER');
1031
    if($auth->triggerUserMod('delete', array($deleted))) {
1032
        // force and immediate logout including removing the sticky cookie
1033
        auth_logoff();
1034
        return true;
1035
    }
1036
1037
    return false;
1038
}
1039
1040
/**
1041
 * Send a  new password
1042
 *
1043
 * This function handles both phases of the password reset:
1044
 *
1045
 *   - handling the first request of password reset
1046
 *   - validating the password reset auth token
1047
 *
1048
 * @author Benoit Chesneau <[email protected]>
1049
 * @author Chris Smith <[email protected]>
1050
 * @author Andreas Gohr <[email protected]>
1051
 *
1052
 * @return bool true on success, false on any error
1053
 */
1054
function act_resendpwd() {
1055
    global $lang;
1056
    global $conf;
1057
    /* @var DokuWiki_Auth_Plugin $auth */
1058
    global $auth;
1059
    /* @var Input $INPUT */
1060
    global $INPUT;
1061
1062
    if(!actionOK('resendpwd')) {
1063
        msg($lang['resendna'], -1);
1064
        return false;
1065
    }
1066
1067
    $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
1068
1069
    if($token) {
1070
        // we're in token phase - get user info from token
1071
1072
        $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
1073
        if(!file_exists($tfile)) {
1074
            msg($lang['resendpwdbadauth'], -1);
1075
            $INPUT->remove('pwauth');
1076
            return false;
1077
        }
1078
        // token is only valid for 3 days
1079
        if((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
1080
            msg($lang['resendpwdbadauth'], -1);
1081
            $INPUT->remove('pwauth');
1082
            @unlink($tfile);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1083
            return false;
1084
        }
1085
1086
        $user     = io_readfile($tfile);
1087
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1088
        if(!$userinfo['mail']) {
1089
            msg($lang['resendpwdnouser'], -1);
1090
            return false;
1091
        }
1092
1093
        if(!$conf['autopasswd']) { // we let the user choose a password
1094
            $pass = $INPUT->str('pass');
1095
1096
            // password given correctly?
1097
            if(!$pass) return false;
1098
            if($pass != $INPUT->str('passchk')) {
1099
                msg($lang['regbadpass'], -1);
1100
                return false;
1101
            }
1102
1103
            // change it
1104
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1105
                msg($lang['proffail'], -1);
1106
                return false;
1107
            }
1108
1109
        } else { // autogenerate the password and send by mail
1110
1111
            $pass = auth_pwgen($user);
1112
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1113
                msg($lang['proffail'], -1);
1114
                return false;
1115
            }
1116
1117
            if(auth_sendPassword($user, $pass)) {
1118
                msg($lang['resendpwdsuccess'], 1);
1119
            } else {
1120
                msg($lang['regmailfail'], -1);
1121
            }
1122
        }
1123
1124
        @unlink($tfile);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1125
        return true;
1126
1127
    } else {
1128
        // we're in request phase
1129
1130
        if(!$INPUT->post->bool('save')) return false;
1131
1132
        if(!$INPUT->post->str('login')) {
1133
            msg($lang['resendpwdmissing'], -1);
1134
            return false;
1135
        } else {
1136
            $user = trim($auth->cleanUser($INPUT->post->str('login')));
1137
        }
1138
1139
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1140
        if(!$userinfo['mail']) {
1141
            msg($lang['resendpwdnouser'], -1);
1142
            return false;
1143
        }
1144
1145
        // generate auth token
1146
        $token = md5(auth_randombytes(16)); // random secret
1147
        $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
1148
        $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
1149
1150
        io_saveFile($tfile, $user);
1151
1152
        $text = rawLocale('pwconfirm');
1153
        $trep = array(
1154
            'FULLNAME' => $userinfo['name'],
1155
            'LOGIN'    => $user,
1156
            'CONFIRM'  => $url
1157
        );
1158
1159
        $mail = new Mailer();
1160
        $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
1161
        $mail->subject($lang['regpwmail']);
1162
        $mail->setBody($text, $trep);
1163
        if($mail->send()) {
1164
            msg($lang['resendpwdconfirm'], 1);
1165
        } else {
1166
            msg($lang['regmailfail'], -1);
1167
        }
1168
        return true;
1169
    }
1170
    // never reached
1171
}
1172
1173
/**
1174
 * Encrypts a password using the given method and salt
1175
 *
1176
 * If the selected method needs a salt and none was given, a random one
1177
 * is chosen.
1178
 *
1179
 * @author  Andreas Gohr <[email protected]>
1180
 *
1181
 * @param string $clear The clear text password
1182
 * @param string $method The hashing method
1183
 * @param string $salt A salt, null for random
1184
 * @return  string  The crypted password
1185
 */
1186
function auth_cryptPassword($clear, $method = '', $salt = null) {
1187
    global $conf;
1188
    if(empty($method)) $method = $conf['passcrypt'];
1189
1190
    $pass = new PassHash();
1191
    $call = 'hash_'.$method;
1192
1193
    if(!method_exists($pass, $call)) {
1194
        msg("Unsupported crypt method $method", -1);
1195
        return false;
1196
    }
1197
1198
    return $pass->$call($clear, $salt);
1199
}
1200
1201
/**
1202
 * Verifies a cleartext password against a crypted hash
1203
 *
1204
 * @author Andreas Gohr <[email protected]>
1205
 *
1206
 * @param  string $clear The clear text password
1207
 * @param  string $crypt The hash to compare with
1208
 * @return bool true if both match
1209
 */
1210
function auth_verifyPassword($clear, $crypt) {
1211
    $pass = new PassHash();
1212
    return $pass->verify_hash($clear, $crypt);
1213
}
1214
1215
/**
1216
 * Set the authentication cookie and add user identification data to the session
1217
 *
1218
 * @param string  $user       username
1219
 * @param string  $pass       encrypted password
1220
 * @param bool    $sticky     whether or not the cookie will last beyond the session
1221
 * @return bool
1222
 */
1223
function auth_setCookie($user, $pass, $sticky) {
1224
    global $conf;
1225
    /* @var DokuWiki_Auth_Plugin $auth */
1226
    global $auth;
1227
    global $USERINFO;
1228
1229
    if(!$auth) return false;
1230
    $USERINFO = $auth->getUserData($user);
1231
1232
    // set cookie
1233
    $cookie    = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
1234
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
1235
    $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; //one year
1236
    setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
1237
1238
    // set session
1239
    $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
1240
    $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
1241
    $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
1242
    $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
1243
    $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
1244
1245
    return true;
1246
}
1247
1248
/**
1249
 * Returns the user, (encrypted) password and sticky bit from cookie
1250
 *
1251
 * @returns array
1252
 */
1253
function auth_getCookie() {
1254
    if(!isset($_COOKIE[DOKU_COOKIE])) {
1255
        return array(null, null, null);
1256
    }
1257
    list($user, $sticky, $pass) = explode('|', $_COOKIE[DOKU_COOKIE], 3);
1258
    $sticky = (bool) $sticky;
1259
    $pass   = base64_decode($pass);
1260
    $user   = base64_decode($user);
1261
    return array($user, $sticky, $pass);
1262
}
1263
1264
//Setup VIM: ex: et ts=2 :
1265