Completed
Push — sidebaracl ( 7a112d...7c3e4a )
by Andreas
04:38
created

inc/auth.php (1 issue)

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