Completed
Push — nozlib ( a04611...a8d259 )
by Andreas
11:20 queued 05:17
created

inc/auth.php (4 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
/**
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
        } elseif ('auth' . $conf['authtype'] === $plugin) {
55
            // matches old auth backends (pre-Weatherwax)
56
            $auth = $plugin_controller->load('auth', $plugin);
57
            msg('Your authtype setting is deprecated. You must set $conf[\'authtype\'] = "auth' . $conf['authtype'] . '"'
58
                 . ' in your configuration (see <a href="https://www.dokuwiki.org/auth">Authentication Backends</a>)',-1,'','',MSG_ADMINS_ONLY);
59
        }
60
    }
61
62
    if(!isset($auth) || !$auth){
63
        msg($lang['authtempfail'], -1);
64
        return false;
65
    }
66
67
    if ($auth->success == false) {
68
        // degrade to unauthenticated user
69
        unset($auth);
70
        auth_logoff();
71
        msg($lang['authtempfail'], -1);
72
        return false;
73
    }
74
75
    // do the login either by cookie or provided credentials XXX
76
    $INPUT->set('http_credentials', false);
77
    if(!$conf['rememberme']) $INPUT->set('r', false);
78
79
    // handle renamed HTTP_AUTHORIZATION variable (can happen when a fix like
80
    // the one presented at
81
    // http://www.besthostratings.com/articles/http-auth-php-cgi.html is used
82
    // for enabling HTTP authentication with CGI/SuExec)
83
    if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
84
        $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
85
    // streamline HTTP auth credentials (IIS/rewrite -> mod_php)
86
    if(isset($_SERVER['HTTP_AUTHORIZATION'])) {
87
        list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
88
            explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
89
    }
90
91
    // if no credentials were given try to use HTTP auth (for SSO)
92
    if(!$INPUT->str('u') && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])) {
93
        $INPUT->set('u', $_SERVER['PHP_AUTH_USER']);
94
        $INPUT->set('p', $_SERVER['PHP_AUTH_PW']);
95
        $INPUT->set('http_credentials', true);
96
    }
97
98
    // apply cleaning (auth specific user names, remove control chars)
99
    if (true === $auth->success) {
100
        $INPUT->set('u', $auth->cleanUser(stripctl($INPUT->str('u'))));
101
        $INPUT->set('p', stripctl($INPUT->str('p')));
102
    }
103
104
    if(!is_null($auth) && $auth->canDo('external')) {
0 ignored issues
show
Documentation Bug introduced by
The method canDo does not exist on object<DokuWiki_Plugin>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
105
        // external trust mechanism in place
106
        $auth->trustExternal($INPUT->str('u'), $INPUT->str('p'), $INPUT->bool('r'));
107
    } else {
108
        $evdata = array(
109
            'user'     => $INPUT->str('u'),
110
            'password' => $INPUT->str('p'),
111
            'sticky'   => $INPUT->bool('r'),
112
            'silent'   => $INPUT->bool('http_credentials')
113
        );
114
        trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
115
    }
116
117
    //load ACL into a global array XXX
118
    $AUTH_ACL = auth_loadACL();
119
120
    return true;
121
}
122
123
/**
124
 * Loads the ACL setup and handle user wildcards
125
 *
126
 * @author Andreas Gohr <[email protected]>
127
 *
128
 * @return array
129
 */
130
function auth_loadACL() {
131
    global $config_cascade;
132
    global $USERINFO;
133
    /* @var Input $INPUT */
134
    global $INPUT;
135
136
    if(!is_readable($config_cascade['acl']['default'])) return array();
137
138
    $acl = file($config_cascade['acl']['default']);
139
140
    $out = array();
141
    foreach($acl as $line) {
142
        $line = trim($line);
143
        if(empty($line) || ($line{0} == '#')) continue; // skip blank lines & comments
144
        list($id,$rest) = preg_split('/[ \t]+/',$line,2);
145
146
        // substitute user wildcard first (its 1:1)
147
        if(strstr($line, '%USER%')){
148
            // if user is not logged in, this ACL line is meaningless - skip it
149
            if (!$INPUT->server->has('REMOTE_USER')) continue;
150
151
            $id   = str_replace('%USER%',cleanID($INPUT->server->str('REMOTE_USER')),$id);
152
            $rest = str_replace('%USER%',auth_nameencode($INPUT->server->str('REMOTE_USER')),$rest);
153
        }
154
155
        // substitute group wildcard (its 1:m)
156
        if(strstr($line, '%GROUP%')){
157
            // if user is not logged in, grps is empty, no output will be added (i.e. skipped)
158
            foreach((array) $USERINFO['grps'] as $grp){
159
                $nid   = str_replace('%GROUP%',cleanID($grp),$id);
160
                $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
161
                $out[] = "$nid\t$nrest";
162
            }
163
        } else {
164
            $out[] = "$id\t$rest";
165
        }
166
    }
167
168
    return $out;
169
}
170
171
/**
172
 * Event hook callback for AUTH_LOGIN_CHECK
173
 *
174
 * @param array $evdata
175
 * @return bool
176
 */
177
function auth_login_wrapper($evdata) {
178
    return auth_login(
179
        $evdata['user'],
180
        $evdata['password'],
181
        $evdata['sticky'],
182
        $evdata['silent']
183
    );
184
}
185
186
/**
187
 * This tries to login the user based on the sent auth credentials
188
 *
189
 * The authentication works like this: if a username was given
190
 * a new login is assumed and user/password are checked. If they
191
 * are correct the password is encrypted with blowfish and stored
192
 * together with the username in a cookie - the same info is stored
193
 * in the session, too. Additonally a browserID is stored in the
194
 * session.
195
 *
196
 * If no username was given the cookie is checked: if the username,
197
 * crypted password and browserID match between session and cookie
198
 * no further testing is done and the user is accepted
199
 *
200
 * If a cookie was found but no session info was availabe the
201
 * blowfish encrypted password from the cookie is decrypted and
202
 * together with username rechecked by calling this function again.
203
 *
204
 * On a successful login $_SERVER[REMOTE_USER] and $USERINFO
205
 * are set.
206
 *
207
 * @author  Andreas Gohr <[email protected]>
208
 *
209
 * @param   string  $user    Username
210
 * @param   string  $pass    Cleartext Password
211
 * @param   bool    $sticky  Cookie should not expire
212
 * @param   bool    $silent  Don't show error on bad auth
213
 * @return  bool             true on successful auth
214
 */
215
function auth_login($user, $pass, $sticky = false, $silent = false) {
216
    global $USERINFO;
217
    global $conf;
218
    global $lang;
219
    /* @var DokuWiki_Auth_Plugin $auth */
220
    global $auth;
221
    /* @var Input $INPUT */
222
    global $INPUT;
223
224
    $sticky ? $sticky = true : $sticky = false; //sanity check
225
226
    if(!$auth) return false;
227
228
    if(!empty($user)) {
229
        //usual login
230
        if(!empty($pass) && $auth->checkPass($user, $pass)) {
231
            // make logininfo globally available
232
            $INPUT->server->set('REMOTE_USER', $user);
233
            $secret                 = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
234
            auth_setCookie($user, auth_encrypt($pass, $secret), $sticky);
235
            return true;
236
        } else {
237
            //invalid credentials - log off
238
            if(!$silent) msg($lang['badlogin'], -1);
239
            auth_logoff();
240
            return false;
241
        }
242
    } else {
243
        // read cookie information
244
        list($user, $sticky, $pass) = auth_getCookie();
245
        if($user && $pass) {
246
            // we got a cookie - see if we can trust it
247
248
            // get session info
249
            $session = $_SESSION[DOKU_COOKIE]['auth'];
250
            if(isset($session) &&
251
                $auth->useSessionCache($user) &&
252
                ($session['time'] >= time() - $conf['auth_security_timeout']) &&
253
                ($session['user'] == $user) &&
254
                ($session['pass'] == sha1($pass)) && //still crypted
255
                ($session['buid'] == auth_browseruid())
256
            ) {
257
258
                // he has session, cookie and browser right - let him in
259
                $INPUT->server->set('REMOTE_USER', $user);
260
                $USERINFO               = $session['info']; //FIXME move all references to session
261
                return true;
262
            }
263
            // no we don't trust it yet - recheck pass but silent
264
            $secret = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
265
            $pass   = auth_decrypt($pass, $secret);
266
            return auth_login($user, $pass, $sticky, true);
267
        }
268
    }
269
    //just to be sure
270
    auth_logoff(true);
271
    return false;
272
}
273
274
/**
275
 * Builds a pseudo UID from browser and IP data
276
 *
277
 * This is neither unique nor unfakable - still it adds some
278
 * security. Using the first part of the IP makes sure
279
 * proxy farms like AOLs are still okay.
280
 *
281
 * @author  Andreas Gohr <[email protected]>
282
 *
283
 * @return  string  a MD5 sum of various browser headers
284
 */
285
function auth_browseruid() {
286
    /* @var Input $INPUT */
287
    global $INPUT;
288
289
    $ip  = clientIP(true);
290
    $uid = '';
291
    $uid .= $INPUT->server->str('HTTP_USER_AGENT');
292
    $uid .= $INPUT->server->str('HTTP_ACCEPT_CHARSET');
293
    $uid .= substr($ip, 0, strpos($ip, '.'));
294
    $uid = strtolower($uid);
295
    return md5($uid);
296
}
297
298
/**
299
 * Creates a random key to encrypt the password in cookies
300
 *
301
 * This function tries to read the password for encrypting
302
 * cookies from $conf['metadir'].'/_htcookiesalt'
303
 * if no such file is found a random key is created and
304
 * and stored in this file.
305
 *
306
 * @author  Andreas Gohr <[email protected]>
307
 *
308
 * @param   bool $addsession if true, the sessionid is added to the salt
309
 * @param   bool $secure     if security is more important than keeping the old value
310
 * @return  string
311
 */
312
function auth_cookiesalt($addsession = false, $secure = false) {
313
    global $conf;
314
    $file = $conf['metadir'].'/_htcookiesalt';
315
    if ($secure || !file_exists($file)) {
316
        $file = $conf['metadir'].'/_htcookiesalt2';
317
    }
318
    $salt = io_readFile($file);
319
    if(empty($salt)) {
320
        $salt = bin2hex(auth_randombytes(64));
321
        io_saveFile($file, $salt);
322
    }
323
    if($addsession) {
324
        $salt .= session_id();
325
    }
326
    return $salt;
327
}
328
329
/**
330
 * Return truly (pseudo) random bytes if available, otherwise fall back to mt_rand
331
 *
332
 * @author Mark Seecof
333
 * @author Michael Hamann <[email protected]>
334
 * @link   http://www.php.net/manual/de/function.mt-rand.php#83655
335
 *
336
 * @param int $length number of bytes to get
337
 * @return string binary random strings
338
 */
339
function auth_randombytes($length) {
340
    $strong = false;
341
    $rbytes = false;
342
343
    if (function_exists('openssl_random_pseudo_bytes')
344
        && (version_compare(PHP_VERSION, '5.3.4') >= 0
345
            || strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
346
    ) {
347
        $rbytes = openssl_random_pseudo_bytes($length, $strong);
348
    }
349
350
    if (!$strong && function_exists('mcrypt_create_iv')
351
        && (version_compare(PHP_VERSION, '5.3.7') >= 0
352
            || strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
353
    ) {
354
        $rbytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
355
        if ($rbytes !== false && strlen($rbytes) === $length) {
356
            $strong = true;
357
        }
358
    }
359
360
    // If no strong randoms available, try OS the specific ways
361
    if(!$strong) {
362
        // Unix/Linux platform
363
        $fp = @fopen('/dev/urandom', 'rb');
364
        if($fp !== false) {
365
            $rbytes = fread($fp, $length);
366
            fclose($fp);
367
        }
368
369
        // MS-Windows platform
370
        if(class_exists('COM')) {
371
            // http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
372
            try {
373
                $CAPI_Util = new COM('CAPICOM.Utilities.1');
374
                $rbytes    = $CAPI_Util->GetRandom($length, 0);
375
376
                // if we ask for binary data PHP munges it, so we
377
                // request base64 return value.
378
                if($rbytes) $rbytes = base64_decode($rbytes);
379
            } catch(Exception $ex) {
380
                // fail
381
            }
382
        }
383
    }
384
    if(strlen($rbytes) < $length) $rbytes = false;
385
386
    // still no random bytes available - fall back to mt_rand()
387
    if($rbytes === false) {
388
        $rbytes = '';
389
        for ($i = 0; $i < $length; ++$i) {
390
            $rbytes .= chr(mt_rand(0, 255));
391
        }
392
    }
393
394
    return $rbytes;
395
}
396
397
/**
398
 * Random number generator using the best available source
399
 *
400
 * @author Michael Samuel
401
 * @author Michael Hamann <[email protected]>
402
 *
403
 * @param int $min
404
 * @param int $max
405
 * @return int
406
 */
407
function auth_random($min, $max) {
408
    $abs_max = $max - $min;
409
410
    $nbits = 0;
411
    for ($n = $abs_max; $n > 0; $n >>= 1) {
412
        ++$nbits;
413
    }
414
415
    $mask = (1 << $nbits) - 1;
416
    do {
417
        $bytes    = auth_randombytes(PHP_INT_SIZE);
418
        $integers = unpack('Inum', $bytes);
419
        $integer  = $integers["num"] & $mask;
420
    } while ($integer > $abs_max);
421
422
    return $min + $integer;
423
}
424
425
/**
426
 * Encrypt data using the given secret using AES
427
 *
428
 * The mode is CBC with a random initialization vector, the key is derived
429
 * using pbkdf2.
430
 *
431
 * @param string $data   The data that shall be encrypted
432
 * @param string $secret The secret/password that shall be used
433
 * @return string The ciphertext
434
 */
435
function auth_encrypt($data, $secret) {
436
    $iv     = auth_randombytes(16);
437
    $cipher = new Crypt_AES();
438
    $cipher->setPassword($secret);
439
440
    /*
441
    this uses the encrypted IV as IV as suggested in
442
    http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, Appendix C
443
    for unique but necessarily random IVs. The resulting ciphertext is
444
    compatible to ciphertext that was created using a "normal" IV.
445
    */
446
    return $cipher->encrypt($iv.$data);
447
}
448
449
/**
450
 * Decrypt the given AES ciphertext
451
 *
452
 * The mode is CBC, the key is derived using pbkdf2
453
 *
454
 * @param string $ciphertext The encrypted data
455
 * @param string $secret     The secret/password that shall be used
456
 * @return string The decrypted data
457
 */
458
function auth_decrypt($ciphertext, $secret) {
459
    $iv     = substr($ciphertext, 0, 16);
460
    $cipher = new Crypt_AES();
461
    $cipher->setPassword($secret);
462
    $cipher->setIV($iv);
463
464
    return $cipher->decrypt(substr($ciphertext, 16));
465
}
466
467
/**
468
 * Log out the current user
469
 *
470
 * This clears all authentication data and thus log the user
471
 * off. It also clears session data.
472
 *
473
 * @author  Andreas Gohr <[email protected]>
474
 *
475
 * @param bool $keepbc - when true, the breadcrumb data is not cleared
476
 */
477
function auth_logoff($keepbc = false) {
478
    global $conf;
479
    global $USERINFO;
480
    /* @var DokuWiki_Auth_Plugin $auth */
481
    global $auth;
482
    /* @var Input $INPUT */
483
    global $INPUT;
484
485
    // make sure the session is writable (it usually is)
486
    @session_start();
487
488
    if(isset($_SESSION[DOKU_COOKIE]['auth']['user']))
489
        unset($_SESSION[DOKU_COOKIE]['auth']['user']);
490
    if(isset($_SESSION[DOKU_COOKIE]['auth']['pass']))
491
        unset($_SESSION[DOKU_COOKIE]['auth']['pass']);
492
    if(isset($_SESSION[DOKU_COOKIE]['auth']['info']))
493
        unset($_SESSION[DOKU_COOKIE]['auth']['info']);
494
    if(!$keepbc && isset($_SESSION[DOKU_COOKIE]['bc']))
495
        unset($_SESSION[DOKU_COOKIE]['bc']);
496
    $INPUT->server->remove('REMOTE_USER');
497
    $USERINFO = null; //FIXME
498
499
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
500
    setcookie(DOKU_COOKIE, '', time() - 600000, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
501
502
    if($auth) $auth->logOff();
503
}
504
505
/**
506
 * Check if a user is a manager
507
 *
508
 * Should usually be called without any parameters to check the current
509
 * user.
510
 *
511
 * The info is available through $INFO['ismanager'], too
512
 *
513
 * @author Andreas Gohr <[email protected]>
514
 * @see    auth_isadmin
515
 *
516
 * @param  string $user       Username
517
 * @param  array  $groups     List of groups the user is in
518
 * @param  bool   $adminonly  when true checks if user is admin
519
 * @return bool
520
 */
521
function auth_ismanager($user = null, $groups = null, $adminonly = false) {
522
    global $conf;
523
    global $USERINFO;
524
    /* @var DokuWiki_Auth_Plugin $auth */
525
    global $auth;
526
    /* @var Input $INPUT */
527
    global $INPUT;
528
529
530
    if(!$auth) return false;
531
    if(is_null($user)) {
532
        if(!$INPUT->server->has('REMOTE_USER')) {
533
            return false;
534
        } else {
535
            $user = $INPUT->server->str('REMOTE_USER');
536
        }
537
    }
538
    if(is_null($groups)) {
539
        $groups = (array) $USERINFO['grps'];
540
    }
541
542
    // check superuser match
543
    if(auth_isMember($conf['superuser'], $user, $groups)) return true;
544
    if($adminonly) return false;
545
    // check managers
546
    if(auth_isMember($conf['manager'], $user, $groups)) return true;
547
548
    return false;
549
}
550
551
/**
552
 * Check if a user is admin
553
 *
554
 * Alias to auth_ismanager with adminonly=true
555
 *
556
 * The info is available through $INFO['isadmin'], too
557
 *
558
 * @author Andreas Gohr <[email protected]>
559
 * @see auth_ismanager()
560
 *
561
 * @param  string $user       Username
562
 * @param  array  $groups     List of groups the user is in
563
 * @return bool
564
 */
565
function auth_isadmin($user = null, $groups = null) {
566
    return auth_ismanager($user, $groups, true);
567
}
568
569
/**
570
 * Match a user and his groups against a comma separated list of
571
 * users and groups to determine membership status
572
 *
573
 * Note: all input should NOT be nameencoded.
574
 *
575
 * @param string $memberlist commaseparated list of allowed users and groups
576
 * @param string $user       user to match against
577
 * @param array  $groups     groups the user is member of
578
 * @return bool       true for membership acknowledged
579
 */
580
function auth_isMember($memberlist, $user, array $groups) {
581
    /* @var DokuWiki_Auth_Plugin $auth */
582
    global $auth;
583
    if(!$auth) return false;
584
585
    // clean user and groups
586
    if(!$auth->isCaseSensitive()) {
587
        $user   = utf8_strtolower($user);
588
        $groups = array_map('utf8_strtolower', $groups);
589
    }
590
    $user   = $auth->cleanUser($user);
591
    $groups = array_map(array($auth, 'cleanGroup'), $groups);
592
593
    // extract the memberlist
594
    $members = explode(',', $memberlist);
595
    $members = array_map('trim', $members);
596
    $members = array_unique($members);
597
    $members = array_filter($members);
598
599
    // compare cleaned values
600
    foreach($members as $member) {
601
        if($member == '@ALL' ) return true;
602
        if(!$auth->isCaseSensitive()) $member = utf8_strtolower($member);
603
        if($member[0] == '@') {
604
            $member = $auth->cleanGroup(substr($member, 1));
605
            if(in_array($member, $groups)) return true;
606
        } else {
607
            $member = $auth->cleanUser($member);
608
            if($member == $user) return true;
609
        }
610
    }
611
612
    // still here? not a member!
613
    return false;
614
}
615
616
/**
617
 * Convinience function for auth_aclcheck()
618
 *
619
 * This checks the permissions for the current user
620
 *
621
 * @author  Andreas Gohr <[email protected]>
622
 *
623
 * @param  string  $id  page ID (needs to be resolved and cleaned)
624
 * @return int          permission level
625
 */
626
function auth_quickaclcheck($id) {
627
    global $conf;
628
    global $USERINFO;
629
    /* @var Input $INPUT */
630
    global $INPUT;
631
    # if no ACL is used always return upload rights
632
    if(!$conf['useacl']) return AUTH_UPLOAD;
633
    return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), $USERINFO['grps']);
634
}
635
636
/**
637
 * Returns the maximum rights a user has for the given ID or its namespace
638
 *
639
 * @author  Andreas Gohr <[email protected]>
640
 *
641
 * @triggers AUTH_ACL_CHECK
642
 * @param  string       $id     page ID (needs to be resolved and cleaned)
643
 * @param  string       $user   Username
644
 * @param  array|null   $groups Array of groups the user is in
645
 * @return int             permission level
646
 */
647
function auth_aclcheck($id, $user, $groups) {
648
    $data = array(
649
        'id'     => $id,
650
        'user'   => $user,
651
        'groups' => $groups
652
    );
653
654
    return trigger_event('AUTH_ACL_CHECK', $data, 'auth_aclcheck_cb');
655
}
656
657
/**
658
 * default ACL check method
659
 *
660
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
661
 *
662
 * @author  Andreas Gohr <[email protected]>
663
 *
664
 * @param  array $data event data
665
 * @return int   permission level
666
 */
667
function auth_aclcheck_cb($data) {
668
    $id     =& $data['id'];
669
    $user   =& $data['user'];
670
    $groups =& $data['groups'];
671
672
    global $conf;
673
    global $AUTH_ACL;
674
    /* @var DokuWiki_Auth_Plugin $auth */
675
    global $auth;
676
677
    // if no ACL is used always return upload rights
678
    if(!$conf['useacl']) return AUTH_UPLOAD;
679
    if(!$auth) return AUTH_NONE;
680
681
    //make sure groups is an array
682
    if(!is_array($groups)) $groups = array();
683
684
    //if user is superuser or in superusergroup return 255 (acl_admin)
685
    if(auth_isadmin($user, $groups)) {
686
        return AUTH_ADMIN;
687
    }
688
689
    if(!$auth->isCaseSensitive()) {
690
        $user   = utf8_strtolower($user);
691
        $groups = array_map('utf8_strtolower', $groups);
692
    }
693
    $user   = auth_nameencode($auth->cleanUser($user));
694
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
695
696
    //prepend groups with @ and nameencode
697
    foreach($groups as &$group) {
698
        $group = '@'.auth_nameencode($group);
699
    }
700
701
    $ns   = getNS($id);
702
    $perm = -1;
703
704
    //add ALL group
705
    $groups[] = '@ALL';
706
707
    //add User
708
    if($user) $groups[] = $user;
709
710
    //check exact match first
711
    $matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
712
    if(count($matches)) {
713
        foreach($matches as $match) {
714
            $match = preg_replace('/#.*$/', '', $match); //ignore comments
715
            $acl   = preg_split('/[ \t]+/', $match);
716
            if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
717
                $acl[1] = utf8_strtolower($acl[1]);
718
            }
719
            if(!in_array($acl[1], $groups)) {
720
                continue;
721
            }
722
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
723
            if($acl[2] > $perm) {
724
                $perm = $acl[2];
725
            }
726
        }
727
        if($perm > -1) {
728
            //we had a match - return it
729
            return (int) $perm;
730
        }
731
    }
732
733
    //still here? do the namespace checks
734
    if($ns) {
735
        $path = $ns.':*';
736
    } else {
737
        $path = '*'; //root document
738
    }
739
740
    do {
741
        $matches = preg_grep('/^'.preg_quote($path, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
742
        if(count($matches)) {
743
            foreach($matches as $match) {
744
                $match = preg_replace('/#.*$/', '', $match); //ignore comments
745
                $acl   = preg_split('/[ \t]+/', $match);
746
                if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
747
                    $acl[1] = utf8_strtolower($acl[1]);
748
                }
749
                if(!in_array($acl[1], $groups)) {
750
                    continue;
751
                }
752
                if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
753
                if($acl[2] > $perm) {
754
                    $perm = $acl[2];
755
                }
756
            }
757
            //we had a match - return it
758
            if($perm != -1) {
759
                return (int) $perm;
760
            }
761
        }
762
        //get next higher namespace
763
        $ns = getNS($ns);
0 ignored issues
show
It seems like $ns defined by getNS($ns) on line 763 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...
764
765
        if($path != '*') {
766
            $path = $ns.':*';
767
            if($path == ':*') $path = '*';
768
        } else {
769
            //we did this already
770
            //looks like there is something wrong with the ACL
771
            //break here
772
            msg('No ACL setup yet! Denying access to everyone.');
773
            return AUTH_NONE;
774
        }
775
    } while(1); //this should never loop endless
776
    return AUTH_NONE;
777
}
778
779
/**
780
 * Encode ASCII special chars
781
 *
782
 * Some auth backends allow special chars in their user and groupnames
783
 * The special chars are encoded with this function. Only ASCII chars
784
 * are encoded UTF-8 multibyte are left as is (different from usual
785
 * urlencoding!).
786
 *
787
 * Decoding can be done with rawurldecode
788
 *
789
 * @author Andreas Gohr <[email protected]>
790
 * @see rawurldecode()
791
 *
792
 * @param string $name
793
 * @param bool $skip_group
794
 * @return string
795
 */
796
function auth_nameencode($name, $skip_group = false) {
797
    global $cache_authname;
798
    $cache =& $cache_authname;
799
    $name  = (string) $name;
800
801
    // never encode wildcard FS#1955
802
    if($name == '%USER%') return $name;
803
    if($name == '%GROUP%') return $name;
804
805
    if(!isset($cache[$name][$skip_group])) {
806
        if($skip_group && $name{0} == '@') {
807
            $cache[$name][$skip_group] = '@'.preg_replace_callback(
808
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
809
                'auth_nameencode_callback', substr($name, 1)
810
            );
811
        } else {
812
            $cache[$name][$skip_group] = preg_replace_callback(
813
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
814
                'auth_nameencode_callback', $name
815
            );
816
        }
817
    }
818
819
    return $cache[$name][$skip_group];
820
}
821
822
/**
823
 * callback encodes the matches
824
 *
825
 * @param array $matches first complete match, next matching subpatterms
826
 * @return string
827
 */
828
function auth_nameencode_callback($matches) {
829
    return '%'.dechex(ord(substr($matches[1],-1)));
830
}
831
832
/**
833
 * Create a pronouncable password
834
 *
835
 * The $foruser variable might be used by plugins to run additional password
836
 * policy checks, but is not used by the default implementation
837
 *
838
 * @author   Andreas Gohr <[email protected]>
839
 * @link     http://www.phpbuilder.com/annotate/message.php3?id=1014451
840
 * @triggers AUTH_PASSWORD_GENERATE
841
 *
842
 * @param  string $foruser username for which the password is generated
843
 * @return string  pronouncable password
844
 */
845
function auth_pwgen($foruser = '') {
846
    $data = array(
847
        'password' => '',
848
        'foruser'  => $foruser
849
    );
850
851
    $evt = new Doku_Event('AUTH_PASSWORD_GENERATE', $data);
852
    if($evt->advise_before(true)) {
853
        $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
854
        $v = 'aeiou'; //vowels
855
        $a = $c.$v; //both
856
        $s = '!$%&?+*~#-_:.;,'; // specials
857
858
        //use thre syllables...
859
        for($i = 0; $i < 3; $i++) {
860
            $data['password'] .= $c[auth_random(0, strlen($c) - 1)];
861
            $data['password'] .= $v[auth_random(0, strlen($v) - 1)];
862
            $data['password'] .= $a[auth_random(0, strlen($a) - 1)];
863
        }
864
        //... and add a nice number and special
865
        $data['password'] .= auth_random(10, 99).$s[auth_random(0, strlen($s) - 1)];
866
    }
867
    $evt->advise_after();
868
869
    return $data['password'];
870
}
871
872
/**
873
 * Sends a password to the given user
874
 *
875
 * @author  Andreas Gohr <[email protected]>
876
 *
877
 * @param string $user Login name of the user
878
 * @param string $password The new password in clear text
879
 * @return bool  true on success
880
 */
881
function auth_sendPassword($user, $password) {
882
    global $lang;
883
    /* @var DokuWiki_Auth_Plugin $auth */
884
    global $auth;
885
    if(!$auth) return false;
886
887
    $user     = $auth->cleanUser($user);
888
    $userinfo = $auth->getUserData($user, $requireGroups = false);
889
890
    if(!$userinfo['mail']) return false;
891
892
    $text = rawLocale('password');
893
    $trep = array(
894
        'FULLNAME' => $userinfo['name'],
895
        'LOGIN'    => $user,
896
        'PASSWORD' => $password
897
    );
898
899
    $mail = new Mailer();
900
    $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
901
    $mail->subject($lang['regpwmail']);
902
    $mail->setBody($text, $trep);
903
    return $mail->send();
904
}
905
906
/**
907
 * Register a new user
908
 *
909
 * This registers a new user - Data is read directly from $_POST
910
 *
911
 * @author  Andreas Gohr <[email protected]>
912
 *
913
 * @return bool  true on success, false on any error
914
 */
915
function register() {
916
    global $lang;
917
    global $conf;
918
    /* @var DokuWiki_Auth_Plugin $auth */
919
    global $auth;
920
    global $INPUT;
921
922
    if(!$INPUT->post->bool('save')) return false;
923
    if(!actionOK('register')) return false;
924
925
    // gather input
926
    $login    = trim($auth->cleanUser($INPUT->post->str('login')));
927
    $fullname = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('fullname')));
928
    $email    = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('email')));
929
    $pass     = $INPUT->post->str('pass');
930
    $passchk  = $INPUT->post->str('passchk');
931
932
    if(empty($login) || empty($fullname) || empty($email)) {
933
        msg($lang['regmissing'], -1);
934
        return false;
935
    }
936
937
    if($conf['autopasswd']) {
938
        $pass = auth_pwgen($login); // automatically generate password
939
    } elseif(empty($pass) || empty($passchk)) {
940
        msg($lang['regmissing'], -1); // complain about missing passwords
941
        return false;
942
    } elseif($pass != $passchk) {
943
        msg($lang['regbadpass'], -1); // complain about misspelled passwords
944
        return false;
945
    }
946
947
    //check mail
948
    if(!mail_isvalid($email)) {
949
        msg($lang['regbadmail'], -1);
950
        return false;
951
    }
952
953
    //okay try to create the user
954
    if(!$auth->triggerUserMod('create', array($login, $pass, $fullname, $email))) {
955
        msg($lang['regfail'], -1);
956
        return false;
957
    }
958
959
    // send notification about the new user
960
    $subscription = new Subscription();
961
    $subscription->send_register($login, $fullname, $email);
962
963
    // are we done?
964
    if(!$conf['autopasswd']) {
965
        msg($lang['regsuccess2'], 1);
966
        return true;
967
    }
968
969
    // autogenerated password? then send password to user
970
    if(auth_sendPassword($login, $pass)) {
971
        msg($lang['regsuccess'], 1);
972
        return true;
973
    } else {
974
        msg($lang['regmailfail'], -1);
975
        return false;
976
    }
977
}
978
979
/**
980
 * Update user profile
981
 *
982
 * @author    Christopher Smith <[email protected]>
983
 */
984
function updateprofile() {
985
    global $conf;
986
    global $lang;
987
    /* @var DokuWiki_Auth_Plugin $auth */
988
    global $auth;
989
    /* @var Input $INPUT */
990
    global $INPUT;
991
992
    if(!$INPUT->post->bool('save')) return false;
993
    if(!checkSecurityToken()) return false;
994
995
    if(!actionOK('profile')) {
996
        msg($lang['profna'], -1);
997
        return false;
998
    }
999
1000
    $changes         = array();
1001
    $changes['pass'] = $INPUT->post->str('newpass');
1002
    $changes['name'] = $INPUT->post->str('fullname');
1003
    $changes['mail'] = $INPUT->post->str('email');
1004
1005
    // check misspelled passwords
1006
    if($changes['pass'] != $INPUT->post->str('passchk')) {
1007
        msg($lang['regbadpass'], -1);
1008
        return false;
1009
    }
1010
1011
    // clean fullname and email
1012
    $changes['name'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['name']));
1013
    $changes['mail'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['mail']));
1014
1015
    // no empty name and email (except the backend doesn't support them)
1016
    if((empty($changes['name']) && $auth->canDo('modName')) ||
1017
        (empty($changes['mail']) && $auth->canDo('modMail'))
1018
    ) {
1019
        msg($lang['profnoempty'], -1);
1020
        return false;
1021
    }
1022
    if(!mail_isvalid($changes['mail']) && $auth->canDo('modMail')) {
1023
        msg($lang['regbadmail'], -1);
1024
        return false;
1025
    }
1026
1027
    $changes = array_filter($changes);
1028
1029
    // check for unavailable capabilities
1030
    if(!$auth->canDo('modName')) unset($changes['name']);
1031
    if(!$auth->canDo('modMail')) unset($changes['mail']);
1032
    if(!$auth->canDo('modPass')) unset($changes['pass']);
1033
1034
    // anything to do?
1035
    if(!count($changes)) {
1036
        msg($lang['profnochange'], -1);
1037
        return false;
1038
    }
1039
1040
    if($conf['profileconfirm']) {
1041
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1042
            msg($lang['badpassconfirm'], -1);
1043
            return false;
1044
        }
1045
    }
1046
1047
    if(!$auth->triggerUserMod('modify', array($INPUT->server->str('REMOTE_USER'), &$changes))) {
1048
        msg($lang['proffail'], -1);
1049
        return false;
1050
    }
1051
1052
    // update cookie and session with the changed data
1053
    if($changes['pass']) {
1054
        list( /*user*/, $sticky, /*pass*/) = auth_getCookie();
1055
        $pass = auth_encrypt($changes['pass'], auth_cookiesalt(!$sticky, true));
1056
        auth_setCookie($INPUT->server->str('REMOTE_USER'), $pass, (bool) $sticky);
1057
    }
1058
    return true;
1059
}
1060
1061
/**
1062
 * Delete the current logged-in user
1063
 *
1064
 * @return bool true on success, false on any error
1065
 */
1066
function auth_deleteprofile(){
1067
    global $conf;
1068
    global $lang;
1069
    /* @var DokuWiki_Auth_Plugin $auth */
1070
    global $auth;
1071
    /* @var Input $INPUT */
1072
    global $INPUT;
1073
1074
    if(!$INPUT->post->bool('delete')) return false;
1075
    if(!checkSecurityToken()) return false;
1076
1077
    // action prevented or auth module disallows
1078
    if(!actionOK('profile_delete') || !$auth->canDo('delUser')) {
1079
        msg($lang['profnodelete'], -1);
1080
        return false;
1081
    }
1082
1083
    if(!$INPUT->post->bool('confirm_delete')){
1084
        msg($lang['profconfdeletemissing'], -1);
1085
        return false;
1086
    }
1087
1088
    if($conf['profileconfirm']) {
1089
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1090
            msg($lang['badpassconfirm'], -1);
1091
            return false;
1092
        }
1093
    }
1094
1095
    $deleted = array();
1096
    $deleted[] = $INPUT->server->str('REMOTE_USER');
1097
    if($auth->triggerUserMod('delete', array($deleted))) {
1098
        // force and immediate logout including removing the sticky cookie
1099
        auth_logoff();
1100
        return true;
1101
    }
1102
1103
    return false;
1104
}
1105
1106
/**
1107
 * Send a  new password
1108
 *
1109
 * This function handles both phases of the password reset:
1110
 *
1111
 *   - handling the first request of password reset
1112
 *   - validating the password reset auth token
1113
 *
1114
 * @author Benoit Chesneau <[email protected]>
1115
 * @author Chris Smith <[email protected]>
1116
 * @author Andreas Gohr <[email protected]>
1117
 *
1118
 * @return bool true on success, false on any error
1119
 */
1120
function act_resendpwd() {
1121
    global $lang;
1122
    global $conf;
1123
    /* @var DokuWiki_Auth_Plugin $auth */
1124
    global $auth;
1125
    /* @var Input $INPUT */
1126
    global $INPUT;
1127
1128
    if(!actionOK('resendpwd')) {
1129
        msg($lang['resendna'], -1);
1130
        return false;
1131
    }
1132
1133
    $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
1134
1135
    if($token) {
1136
        // we're in token phase - get user info from token
1137
1138
        $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
1139
        if(!file_exists($tfile)) {
1140
            msg($lang['resendpwdbadauth'], -1);
1141
            $INPUT->remove('pwauth');
1142
            return false;
1143
        }
1144
        // token is only valid for 3 days
1145
        if((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
1146
            msg($lang['resendpwdbadauth'], -1);
1147
            $INPUT->remove('pwauth');
1148
            @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...
1149
            return false;
1150
        }
1151
1152
        $user     = io_readfile($tfile);
1153
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1154
        if(!$userinfo['mail']) {
1155
            msg($lang['resendpwdnouser'], -1);
1156
            return false;
1157
        }
1158
1159
        if(!$conf['autopasswd']) { // we let the user choose a password
1160
            $pass = $INPUT->str('pass');
1161
1162
            // password given correctly?
1163
            if(!$pass) return false;
1164
            if($pass != $INPUT->str('passchk')) {
1165
                msg($lang['regbadpass'], -1);
1166
                return false;
1167
            }
1168
1169
            // change it
1170
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1171
                msg($lang['proffail'], -1);
1172
                return false;
1173
            }
1174
1175
        } else { // autogenerate the password and send by mail
1176
1177
            $pass = auth_pwgen($user);
1178
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1179
                msg($lang['proffail'], -1);
1180
                return false;
1181
            }
1182
1183
            if(auth_sendPassword($user, $pass)) {
1184
                msg($lang['resendpwdsuccess'], 1);
1185
            } else {
1186
                msg($lang['regmailfail'], -1);
1187
            }
1188
        }
1189
1190
        @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...
1191
        return true;
1192
1193
    } else {
1194
        // we're in request phase
1195
1196
        if(!$INPUT->post->bool('save')) return false;
1197
1198
        if(!$INPUT->post->str('login')) {
1199
            msg($lang['resendpwdmissing'], -1);
1200
            return false;
1201
        } else {
1202
            $user = trim($auth->cleanUser($INPUT->post->str('login')));
1203
        }
1204
1205
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1206
        if(!$userinfo['mail']) {
1207
            msg($lang['resendpwdnouser'], -1);
1208
            return false;
1209
        }
1210
1211
        // generate auth token
1212
        $token = md5(auth_randombytes(16)); // random secret
1213
        $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
1214
        $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
1215
1216
        io_saveFile($tfile, $user);
1217
1218
        $text = rawLocale('pwconfirm');
1219
        $trep = array(
1220
            'FULLNAME' => $userinfo['name'],
1221
            'LOGIN'    => $user,
1222
            'CONFIRM'  => $url
1223
        );
1224
1225
        $mail = new Mailer();
1226
        $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
1227
        $mail->subject($lang['regpwmail']);
1228
        $mail->setBody($text, $trep);
1229
        if($mail->send()) {
1230
            msg($lang['resendpwdconfirm'], 1);
1231
        } else {
1232
            msg($lang['regmailfail'], -1);
1233
        }
1234
        return true;
1235
    }
1236
    // never reached
1237
}
1238
1239
/**
1240
 * Encrypts a password using the given method and salt
1241
 *
1242
 * If the selected method needs a salt and none was given, a random one
1243
 * is chosen.
1244
 *
1245
 * @author  Andreas Gohr <[email protected]>
1246
 *
1247
 * @param string $clear The clear text password
1248
 * @param string $method The hashing method
1249
 * @param string $salt A salt, null for random
1250
 * @return  string  The crypted password
1251
 */
1252
function auth_cryptPassword($clear, $method = '', $salt = null) {
1253
    global $conf;
1254
    if(empty($method)) $method = $conf['passcrypt'];
1255
1256
    $pass = new PassHash();
1257
    $call = 'hash_'.$method;
1258
1259
    if(!method_exists($pass, $call)) {
1260
        msg("Unsupported crypt method $method", -1);
1261
        return false;
1262
    }
1263
1264
    return $pass->$call($clear, $salt);
1265
}
1266
1267
/**
1268
 * Verifies a cleartext password against a crypted hash
1269
 *
1270
 * @author Andreas Gohr <[email protected]>
1271
 *
1272
 * @param  string $clear The clear text password
1273
 * @param  string $crypt The hash to compare with
1274
 * @return bool true if both match
1275
 */
1276
function auth_verifyPassword($clear, $crypt) {
1277
    $pass = new PassHash();
1278
    return $pass->verify_hash($clear, $crypt);
1279
}
1280
1281
/**
1282
 * Set the authentication cookie and add user identification data to the session
1283
 *
1284
 * @param string  $user       username
1285
 * @param string  $pass       encrypted password
1286
 * @param bool    $sticky     whether or not the cookie will last beyond the session
1287
 * @return bool
1288
 */
1289
function auth_setCookie($user, $pass, $sticky) {
1290
    global $conf;
1291
    /* @var DokuWiki_Auth_Plugin $auth */
1292
    global $auth;
1293
    global $USERINFO;
1294
1295
    if(!$auth) return false;
1296
    $USERINFO = $auth->getUserData($user);
1297
1298
    // set cookie
1299
    $cookie    = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
1300
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
1301
    $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; //one year
1302
    setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
1303
1304
    // set session
1305
    $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
1306
    $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
1307
    $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
1308
    $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
1309
    $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
1310
1311
    return true;
1312
}
1313
1314
/**
1315
 * Returns the user, (encrypted) password and sticky bit from cookie
1316
 *
1317
 * @returns array
1318
 */
1319
function auth_getCookie() {
1320
    if(!isset($_COOKIE[DOKU_COOKIE])) {
1321
        return array(null, null, null);
1322
    }
1323
    list($user, $sticky, $pass) = explode('|', $_COOKIE[DOKU_COOKIE], 3);
1324
    $sticky = (bool) $sticky;
1325
    $pass   = base64_decode($pass);
1326
    $user   = base64_decode($user);
1327
    return array($user, $sticky, $pass);
1328
}
1329
1330
//Setup VIM: ex: et ts=2 :
1331