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 <andi@splitbrain.org> 10 */ 11 12 if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/'); 13 require_once(DOKU_INC.'inc/common.php'); 14 require_once(DOKU_INC.'inc/io.php'); 15 16 // some ACL level defines 17 define('AUTH_NONE',0); 18 define('AUTH_READ',1); 19 define('AUTH_EDIT',2); 20 define('AUTH_CREATE',4); 21 define('AUTH_UPLOAD',8); 22 define('AUTH_DELETE',16); 23 define('AUTH_ADMIN',255); 24 25 global $conf; 26 27 if($conf['useacl']){ 28 require_once(DOKU_INC.'inc/blowfish.php'); 29 require_once(DOKU_INC.'inc/mail.php'); 30 31 global $auth; 32 33 // load the the backend auth functions and instantiate the auth object 34 if (@file_exists(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php')) { 35 require_once(DOKU_INC.'inc/auth/basic.class.php'); 36 require_once(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php'); 37 38 $auth_class = "auth_".$conf['authtype']; 39 if (class_exists($auth_class)) { 40 $auth = new $auth_class(); 41 if ($auth->success == false) { 42 // degrade to unauthenticated user 43 unset($auth); 44 auth_logoff(); 45 msg($lang['authtempfail'], -1); 46 } 47 } else { 48 nice_die($lang['authmodfailed']); 49 } 50 } else { 51 nice_die($lang['authmodfailed']); 52 } 53 } 54 55 // do the login either by cookie or provided credentials 56 if($conf['useacl'] && $auth){ 57 if (!isset($_REQUEST['u'])) $_REQUEST['u'] = ''; 58 if (!isset($_REQUEST['p'])) $_REQUEST['p'] = ''; 59 if (!isset($_REQUEST['r'])) $_REQUEST['r'] = ''; 60 61 // if no credentials were given try to use HTTP auth (for SSO) 62 if(empty($_REQUEST['u']) && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])){ 63 $_REQUEST['u'] = $_SERVER['PHP_AUTH_USER']; 64 $_REQUEST['p'] = $_SERVER['PHP_AUTH_PW']; 65 } 66 67 // external trust mechanism in place? 68 if(!is_null($auth) && $auth->canDo('external')){ 69 $auth->trustExternal($_REQUEST['u'],$_REQUEST['p'],$_REQUEST['r']); 70 }else{ 71 auth_login($_REQUEST['u'],$_REQUEST['p'],$_REQUEST['r']); 72 } 73 74 //load ACL into a global array 75 global $AUTH_ACL; 76 if(is_readable(DOKU_CONF.'acl.auth.php')){ 77 $AUTH_ACL = file(DOKU_CONF.'acl.auth.php'); 78 if(isset($_SERVER['REMOTE_USER'])){ 79 $AUTH_ACL = str_replace('@USER@',$_SERVER['REMOTE_USER'],$AUTH_ACL); 80 } 81 }else{ 82 $AUTH_ACL = array(); 83 } 84 } 85 86/** 87 * This tries to login the user based on the sent auth credentials 88 * 89 * The authentication works like this: if a username was given 90 * a new login is assumed and user/password are checked. If they 91 * are correct the password is encrypted with blowfish and stored 92 * together with the username in a cookie - the same info is stored 93 * in the session, too. Additonally a browserID is stored in the 94 * session. 95 * 96 * If no username was given the cookie is checked: if the username, 97 * crypted password and browserID match between session and cookie 98 * no further testing is done and the user is accepted 99 * 100 * If a cookie was found but no session info was availabe the 101 * blowfish encrypted password from the cookie is decrypted and 102 * together with username rechecked by calling this function again. 103 * 104 * On a successful login $_SERVER[REMOTE_USER] and $USERINFO 105 * are set. 106 * 107 * @author Andreas Gohr <andi@splitbrain.org> 108 * 109 * @param string $user Username 110 * @param string $pass Cleartext Password 111 * @param bool $sticky Cookie should not expire 112 * @param bool $silent Don't show error on bad auth 113 * @return bool true on successful auth 114*/ 115function auth_login($user,$pass,$sticky=false,$silent=false){ 116 global $USERINFO; 117 global $conf; 118 global $lang; 119 global $auth; 120 $sticky ? $sticky = true : $sticky = false; //sanity check 121 122 if(!empty($user)){ 123 //usual login 124 if ($auth->checkPass($user,$pass)){ 125 // make logininfo globally available 126 $_SERVER['REMOTE_USER'] = $user; 127 $USERINFO = $auth->getUserData($user); 128 129 // set cookie 130 $pass = PMA_blowfish_encrypt($pass,auth_cookiesalt()); 131 $cookie = base64_encode("$user|$sticky|$pass"); 132 if($sticky) $time = time()+60*60*24*365; //one year 133 setcookie(DOKU_COOKIE,$cookie,$time,DOKU_REL); 134 135 // set session 136 $_SESSION[DOKU_COOKIE]['auth']['user'] = $user; 137 $_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass; 138 $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid(); 139 $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO; 140 $_SESSION[DOKU_COOKIE]['auth']['time'] = time(); 141 142 return true; 143 }else{ 144 //invalid credentials - log off 145 if(!$silent) msg($lang['badlogin'],-1); 146 auth_logoff(); 147 return false; 148 } 149 }else{ 150 // read cookie information 151 $cookie = base64_decode($_COOKIE[DOKU_COOKIE]); 152 list($user,$sticky,$pass) = split('\|',$cookie,3); 153 // get session info 154 $session = $_SESSION[DOKU_COOKIE]['auth']; 155 if($user && $pass){ 156 // we got a cookie - see if we can trust it 157 if(isset($session) && 158 ($session['time'] >= time()-$conf['auth_security_timeout']) && 159 ($session['user'] == $user) && 160 ($session['pass'] == $pass) && //still crypted 161 ($session['buid'] == auth_browseruid()) ){ 162 // he has session, cookie and browser right - let him in 163 $_SERVER['REMOTE_USER'] = $user; 164 $USERINFO = $session['info']; //FIXME move all references to session 165 return true; 166 } 167 // no we don't trust it yet - recheck pass but silent 168 $pass = PMA_blowfish_decrypt($pass,auth_cookiesalt()); 169 return auth_login($user,$pass,$sticky,true); 170 } 171 } 172 //just to be sure 173 auth_logoff(); 174 return false; 175} 176 177/** 178 * Builds a pseudo UID from browser and IP data 179 * 180 * This is neither unique nor unfakable - still it adds some 181 * security. Using the first part of the IP makes sure 182 * proxy farms like AOLs are stil okay. 183 * 184 * @author Andreas Gohr <andi@splitbrain.org> 185 * 186 * @return string a MD5 sum of various browser headers 187 */ 188function auth_browseruid(){ 189 $uid = ''; 190 $uid .= $_SERVER['HTTP_USER_AGENT']; 191 $uid .= $_SERVER['HTTP_ACCEPT_ENCODING']; 192 $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE']; 193 $uid .= $_SERVER['HTTP_ACCEPT_CHARSET']; 194 $uid .= substr($_SERVER['REMOTE_ADDR'],0,strpos($_SERVER['REMOTE_ADDR'],'.')); 195 return md5($uid); 196} 197 198/** 199 * Creates a random key to encrypt the password in cookies 200 * 201 * This function tries to read the password for encrypting 202 * cookies from $conf['metadir'].'/_htcookiesalt' 203 * if no such file is found a random key is created and 204 * and stored in this file. 205 * 206 * @author Andreas Gohr <andi@splitbrain.org> 207 * 208 * @return string 209 */ 210function auth_cookiesalt(){ 211 global $conf; 212 $file = $conf['metadir'].'/_htcookiesalt'; 213 $salt = io_readFile($file); 214 if(empty($salt)){ 215 $salt = uniqid(rand(),true); 216 io_saveFile($file,$salt); 217 } 218 return $salt; 219} 220 221/** 222 * This clears all authenticationdata and thus log the user 223 * off 224 * 225 * @author Andreas Gohr <andi@splitbrain.org> 226 */ 227function auth_logoff(){ 228 global $conf; 229 global $USERINFO; 230 global $INFO, $ID; 231 global $auth; 232 233 if(isset($_SESSION[DOKU_COOKIE]['auth']['user'])) 234 unset($_SESSION[DOKU_COOKIE]['auth']['user']); 235 if(isset($_SESSION[DOKU_COOKIE]['auth']['pass'])) 236 unset($_SESSION[DOKU_COOKIE]['auth']['pass']); 237 if(isset($_SESSION[DOKU_COOKIE]['auth']['info'])) 238 unset($_SESSION[DOKU_COOKIE]['auth']['info']); 239 if(isset($_SERVER['REMOTE_USER'])) 240 unset($_SERVER['REMOTE_USER']); 241 $USERINFO=null; //FIXME 242 setcookie(DOKU_COOKIE,'',time()-600000,DOKU_REL); 243 244 if($auth && $auth->canDo('logoff')){ 245 $auth->logOff(); 246 } 247} 248 249/** 250 * Check if a user is a manager 251 * 252 * Should usually be called without any parameters to check the current 253 * user. 254 * 255 * The info is available through $INFO['ismanager'], too 256 * 257 * @author Andreas Gohr <andi@splitbrain.org> 258 * @see auth_isadmin 259 * @param string user - Username 260 * @param array groups - List of groups the user is in 261 * @param bool adminonly - when true checks if user is admin 262 */ 263function auth_ismanager($user=null,$groups=null,$adminonly=false){ 264 global $conf; 265 global $USERINFO; 266 267 if(!$conf['useacl']) return false; 268 if(is_null($user)) $user = $_SERVER['REMOTE_USER']; 269 if(is_null($groups)) $groups = $USERINFO['grps']; 270 $user = auth_nameencode($user); 271 272 // check username against superuser and manager 273 if(auth_nameencode($conf['superuser']) == $user) return true; 274 if(!$adminonly){ 275 if(auth_nameencode($conf['manager']) == $user) return true; 276 } 277 278 //prepend groups with @ and nameencode 279 $cnt = count($groups); 280 for($i=0; $i<$cnt; $i++){ 281 $groups[$i] = '@'.auth_nameencode($groups[$i]); 282 } 283 284 // check groups against superuser and manager 285 if(in_array(auth_nameencode($conf['superuser'],true), $groups)) return true; 286 if(!$adminonly){ 287 if(in_array(auth_nameencode($conf['manager'],true), $groups)) return true; 288 } 289 return false; 290} 291 292/** 293 * Check if a user is admin 294 * 295 * Alias to auth_ismanager with adminonly=true 296 * 297 * The info is available through $INFO['isadmin'], too 298 * 299 * @author Andreas Gohr <andi@splitbrain.org> 300 * @see auth_ismanager 301 */ 302function auth_isadmin($user=null,$groups=null){ 303 return auth_ismanager($user,$groups,true); 304} 305 306/** 307 * Convinience function for auth_aclcheck() 308 * 309 * This checks the permissions for the current user 310 * 311 * @author Andreas Gohr <andi@splitbrain.org> 312 * 313 * @param string $id page ID 314 * @return int permission level 315 */ 316function auth_quickaclcheck($id){ 317 global $conf; 318 global $USERINFO; 319 # if no ACL is used always return upload rights 320 if(!$conf['useacl']) return AUTH_UPLOAD; 321 return auth_aclcheck($id,$_SERVER['REMOTE_USER'],$USERINFO['grps']); 322} 323 324/** 325 * Returns the maximum rights a user has for 326 * the given ID or its namespace 327 * 328 * @author Andreas Gohr <andi@splitbrain.org> 329 * 330 * @param string $id page ID 331 * @param string $user Username 332 * @param array $groups Array of groups the user is in 333 * @return int permission level 334 */ 335function auth_aclcheck($id,$user,$groups){ 336 global $conf; 337 global $AUTH_ACL; 338 339 # if no ACL is used always return upload rights 340 if(!$conf['useacl']) return AUTH_UPLOAD; 341 342 $user = auth_nameencode($user); 343 344 //if user is superuser return 255 (acl_admin) 345 if(auth_nameencode($conf['superuser']) == $user) { return AUTH_ADMIN; } 346 347 //make sure groups is an array 348 if(!is_array($groups)) $groups = array(); 349 350 //prepend groups with @ and nameencode 351 $cnt = count($groups); 352 for($i=0; $i<$cnt; $i++){ 353 $groups[$i] = '@'.auth_nameencode($groups[$i]); 354 } 355 //if user is in superuser group return 255 (acl_admin) 356 if(in_array(auth_nameencode($conf['superuser'],true), $groups)) { return AUTH_ADMIN; } 357 358 $ns = getNS($id); 359 $perm = -1; 360 361 if($user){ 362 //add ALL group 363 $groups[] = '@ALL'; 364 //add User 365 $groups[] = $user; 366 //build regexp 367 $regexp = join('|',$groups); 368 }else{ 369 $regexp = '@ALL'; 370 } 371 372 //check exact match first 373 $matches = preg_grep('/^'.preg_quote($id,'/').'\s+('.$regexp.')\s+/',$AUTH_ACL); 374 if(count($matches)){ 375 foreach($matches as $match){ 376 $match = preg_replace('/#.*$/','',$match); //ignore comments 377 $acl = preg_split('/\s+/',$match); 378 if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL! 379 if($acl[2] > $perm){ 380 $perm = $acl[2]; 381 } 382 } 383 if($perm > -1){ 384 //we had a match - return it 385 return $perm; 386 } 387 } 388 389 //still here? do the namespace checks 390 if($ns){ 391 $path = $ns.':\*'; 392 }else{ 393 $path = '\*'; //root document 394 } 395 396 do{ 397 $matches = preg_grep('/^'.$path.'\s+('.$regexp.')\s+/',$AUTH_ACL); 398 if(count($matches)){ 399 foreach($matches as $match){ 400 $match = preg_replace('/#.*$/','',$match); //ignore comments 401 $acl = preg_split('/\s+/',$match); 402 if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL! 403 if($acl[2] > $perm){ 404 $perm = $acl[2]; 405 } 406 } 407 //we had a match - return it 408 return $perm; 409 } 410 411 //get next higher namespace 412 $ns = getNS($ns); 413 414 if($path != '\*'){ 415 $path = $ns.':\*'; 416 if($path == ':\*') $path = '\*'; 417 }else{ 418 //we did this already 419 //looks like there is something wrong with the ACL 420 //break here 421 msg('No ACL setup yet! Denying access to everyone.'); 422 return AUTH_NONE; 423 } 424 }while(1); //this should never loop endless 425 426 //still here? return no permissions 427 return AUTH_NONE; 428} 429 430/** 431 * Encode ASCII special chars 432 * 433 * Some auth backends allow special chars in their user and groupnames 434 * The special chars are encoded with this function. Only ASCII chars 435 * are encoded UTF-8 multibyte are left as is (different from usual 436 * urlencoding!). 437 * 438 * Decoding can be done with rawurldecode 439 * 440 * @author Andreas Gohr <gohr@cosmocode.de> 441 * @see rawurldecode() 442 */ 443function auth_nameencode($name,$skip_group=false){ 444 global $cache_authname; 445 $cache =& $cache_authname; 446 $name = (string) $name; 447 448 if (!isset($cache[$name][$skip_group])) { 449 if($skip_group && $name{0} =='@'){ 450 $cache[$name][$skip_group] = '@'.preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/e', 451 "'%'.dechex(ord('\\1'))",substr($name,1)); 452 }else{ 453 $cache[$name][$skip_group] = preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/e', 454 "'%'.dechex(ord('\\1'))",$name); 455 } 456 } 457 458 return $cache[$name][$skip_group]; 459} 460 461/** 462 * Create a pronouncable password 463 * 464 * @author Andreas Gohr <andi@splitbrain.org> 465 * @link http://www.phpbuilder.com/annotate/message.php3?id=1014451 466 * 467 * @return string pronouncable password 468 */ 469function auth_pwgen(){ 470 $pw = ''; 471 $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones 472 $v = 'aeiou'; //vowels 473 $a = $c.$v; //both 474 475 //use two syllables... 476 for($i=0;$i < 2; $i++){ 477 $pw .= $c[rand(0, strlen($c)-1)]; 478 $pw .= $v[rand(0, strlen($v)-1)]; 479 $pw .= $a[rand(0, strlen($a)-1)]; 480 } 481 //... and add a nice number 482 $pw .= rand(10,99); 483 484 return $pw; 485} 486 487/** 488 * Sends a password to the given user 489 * 490 * @author Andreas Gohr <andi@splitbrain.org> 491 * 492 * @return bool true on success 493 */ 494function auth_sendPassword($user,$password){ 495 global $conf; 496 global $lang; 497 global $auth; 498 499 $hdrs = ''; 500 $userinfo = $auth->getUserData($user); 501 502 if(!$userinfo['mail']) return false; 503 504 $text = rawLocale('password'); 505 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 506 $text = str_replace('@FULLNAME@',$userinfo['name'],$text); 507 $text = str_replace('@LOGIN@',$user,$text); 508 $text = str_replace('@PASSWORD@',$password,$text); 509 $text = str_replace('@TITLE@',$conf['title'],$text); 510 511 return mail_send($userinfo['name'].' <'.$userinfo['mail'].'>', 512 $lang['regpwmail'], 513 $text, 514 $conf['mailfrom']); 515} 516 517/** 518 * Register a new user 519 * 520 * This registers a new user - Data is read directly from $_POST 521 * 522 * @author Andreas Gohr <andi@splitbrain.org> 523 * 524 * @return bool true on success, false on any error 525 */ 526function register(){ 527 global $lang; 528 global $conf; 529 global $auth; 530 531 if(!$_POST['save']) return false; 532 if(!$auth->canDo('addUser')) return false; 533 534 //clean username 535 $_POST['login'] = preg_replace('/.*:/','',$_POST['login']); 536 $_POST['login'] = cleanID($_POST['login']); 537 //clean fullname and email 538 $_POST['fullname'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['fullname'])); 539 $_POST['email'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['email'])); 540 541 if( empty($_POST['login']) || 542 empty($_POST['fullname']) || 543 empty($_POST['email']) ){ 544 msg($lang['regmissing'],-1); 545 return false; 546 } 547 548 if ($conf['autopasswd']) { 549 $pass = auth_pwgen(); // automatically generate password 550 } elseif (empty($_POST['pass']) || 551 empty($_POST['passchk'])) { 552 msg($lang['regmissing'], -1); // complain about missing passwords 553 return false; 554 } elseif ($_POST['pass'] != $_POST['passchk']) { 555 msg($lang['regbadpass'], -1); // complain about misspelled passwords 556 return false; 557 } else { 558 $pass = $_POST['pass']; // accept checked and valid password 559 } 560 561 //check mail 562 if(!mail_isvalid($_POST['email'])){ 563 msg($lang['regbadmail'],-1); 564 return false; 565 } 566 567 //okay try to create the user 568 if(!$auth->createUser($_POST['login'],$pass,$_POST['fullname'],$_POST['email'])){ 569 msg($lang['reguexists'],-1); 570 return false; 571 } 572 573 // create substitutions for use in notification email 574 $substitutions = array( 575 'NEWUSER' => $_POST['login'], 576 'NEWNAME' => $_POST['fullname'], 577 'NEWEMAIL' => $_POST['email'], 578 ); 579 580 if (!$conf['autopasswd']) { 581 msg($lang['regsuccess2'],1); 582 notify('', 'register', '', $_POST['login'], false, $substitutions); 583 return true; 584 } 585 586 // autogenerated password? then send him the password 587 if (auth_sendPassword($_POST['login'],$pass)){ 588 msg($lang['regsuccess'],1); 589 notify('', 'register', '', $_POST['login'], false, $substitutions); 590 return true; 591 }else{ 592 msg($lang['regmailfail'],-1); 593 return false; 594 } 595} 596 597/** 598 * Update user profile 599 * 600 * @author Christopher Smith <chris@jalakai.co.uk> 601 */ 602function updateprofile() { 603 global $conf; 604 global $INFO; 605 global $lang; 606 global $auth; 607 608 if(empty($_POST['save'])) return false; 609 610 // should not be able to get here without Profile being possible... 611 if(!$auth->canDo('Profile')) { 612 msg($lang['profna'],-1); 613 return false; 614 } 615 616 if ($_POST['newpass'] != $_POST['passchk']) { 617 msg($lang['regbadpass'], -1); // complain about misspelled passwords 618 return false; 619 } 620 621 //clean fullname and email 622 $_POST['fullname'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['fullname'])); 623 $_POST['email'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['email'])); 624 625 if (empty($_POST['fullname']) || empty($_POST['email'])) { 626 msg($lang['profnoempty'],-1); 627 return false; 628 } 629 630 if (!mail_isvalid($_POST['email'])){ 631 msg($lang['regbadmail'],-1); 632 return false; 633 } 634 635 if ($_POST['fullname'] != $INFO['userinfo']['name']) $changes['name'] = $_POST['fullname']; 636 if ($_POST['email'] != $INFO['userinfo']['mail']) $changes['mail'] = $_POST['email']; 637 if (!empty($_POST['newpass'])) $changes['pass'] = $_POST['newpass']; 638 639 if (!count($changes)) { 640 msg($lang['profnochange'], -1); 641 return false; 642 } 643 644 if ($conf['profileconfirm']) { 645 if (!auth_verifyPassword($_POST['oldpass'],$INFO['userinfo']['pass'])) { 646 msg($lang['badlogin'],-1); 647 return false; 648 } 649 } 650 651 return $auth->modifyUser($_SERVER['REMOTE_USER'], $changes); 652} 653 654/** 655 * Send a new password 656 * 657 * This function handles both phases of the password reset: 658 * 659 * - handling the first request of password reset 660 * - validating the password reset auth token 661 * 662 * @author Benoit Chesneau <benoit@bchesneau.info> 663 * @author Chris Smith <chris@jalakai.co.uk> 664 * @author Andreas Gohr <andi@splitbrain.org> 665 * 666 * @return bool true on success, false on any error 667*/ 668function act_resendpwd(){ 669 global $lang; 670 global $conf; 671 global $auth; 672 673 if(!actionOK('resendpwd')) return false; 674 675 // should not be able to get here without modPass being possible... 676 if(!$auth->canDo('modPass')) { 677 msg($lang['resendna'],-1); 678 return false; 679 } 680 681 $token = preg_replace('/[^a-f0-9]+/','',$_REQUEST['pwauth']); 682 683 if($token){ 684 // we're in token phase 685 686 $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth'; 687 if(!@file_exists($tfile)){ 688 msg($lang['resendpwdbadauth'],-1); 689 return false; 690 } 691 $user = io_readfile($tfile); 692 @unlink($tfile); 693 $userinfo = $auth->getUserData($user); 694 if(!$userinfo['mail']) { 695 msg($lang['resendpwdnouser'], -1); 696 return false; 697 } 698 699 $pass = auth_pwgen(); 700 if (!$auth->modifyUser($user,array('pass' => $pass))) { 701 msg('error modifying user data',-1); 702 return false; 703 } 704 705 if (auth_sendPassword($user,$pass)) { 706 msg($lang['resendpwdsuccess'],1); 707 } else { 708 msg($lang['regmailfail'],-1); 709 } 710 return true; 711 712 } else { 713 // we're in request phase 714 715 if(!$_POST['save']) return false; 716 717 if (empty($_POST['login'])) { 718 msg($lang['resendpwdmissing'], -1); 719 return false; 720 } else { 721 $_POST['login'] = preg_replace('/.*:/','',$_POST['login']); 722 $user = cleanID($_POST['login']); 723 } 724 725 $userinfo = $auth->getUserData($user); 726 if(!$userinfo['mail']) { 727 msg($lang['resendpwdnouser'], -1); 728 return false; 729 } 730 731 // generate auth token 732 $token = md5(auth_cookiesalt().$user); //secret but user based 733 $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth'; 734 $url = wl('',array('do'=>'resendpwd','pwauth'=>$token),true,'&'); 735 736 io_saveFile($tfile,$user); 737 738 $text = rawLocale('pwconfirm'); 739 $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text); 740 $text = str_replace('@FULLNAME@',$userinfo['name'],$text); 741 $text = str_replace('@LOGIN@',$user,$text); 742 $text = str_replace('@TITLE@',$conf['title'],$text); 743 $text = str_replace('@CONFIRM@',$url,$text); 744 745 if(mail_send($userinfo['name'].' <'.$userinfo['mail'].'>', 746 $lang['regpwmail'], 747 $text, 748 $conf['mailfrom'])){ 749 msg($lang['resendpwdconfirm'],1); 750 }else{ 751 msg($lang['regmailfail'],-1); 752 } 753 return true; 754 } 755 756 return false; // never reached 757} 758 759/** 760 * Uses a regular expresion to check if a given mail address is valid 761 * 762 * May not be completly RFC conform! 763 * 764 * @link http://www.webmasterworld.com/forum88/135.htm 765 * 766 * @param string $email the address to check 767 * @return bool true if address is valid 768 */ 769function isvalidemail($email){ 770 return eregi("^[0-9a-z]([-_.]?[0-9a-z])*@[0-9a-z]([-.]?[0-9a-z])*\\.[a-z]{2,4}$", $email); 771} 772 773/** 774 * Encrypts a password using the given method and salt 775 * 776 * If the selected method needs a salt and none was given, a random one 777 * is chosen. 778 * 779 * The following methods are understood: 780 * 781 * smd5 - Salted MD5 hashing 782 * md5 - Simple MD5 hashing 783 * sha1 - SHA1 hashing 784 * ssha - Salted SHA1 hashing 785 * crypt - Unix crypt 786 * mysql - MySQL password (old method) 787 * my411 - MySQL 4.1.1 password 788 * 789 * @author Andreas Gohr <andi@splitbrain.org> 790 * @return string The crypted password 791 */ 792function auth_cryptPassword($clear,$method='',$salt=''){ 793 global $conf; 794 if(empty($method)) $method = $conf['passcrypt']; 795 796 //prepare a salt 797 if(empty($salt)) $salt = md5(uniqid(rand(), true)); 798 799 switch(strtolower($method)){ 800 case 'smd5': 801 return crypt($clear,'$1$'.substr($salt,0,8).'$'); 802 case 'md5': 803 return md5($clear); 804 case 'sha1': 805 return sha1($clear); 806 case 'ssha': 807 $salt=substr($salt,0,4); 808 return '{SSHA}'.base64_encode(pack("H*", sha1($clear.$salt)).$salt); 809 case 'crypt': 810 return crypt($clear,substr($salt,0,2)); 811 case 'mysql': 812 //from http://www.php.net/mysql comment by <soren at byu dot edu> 813 $nr=0x50305735; 814 $nr2=0x12345671; 815 $add=7; 816 $charArr = preg_split("//", $clear); 817 foreach ($charArr as $char) { 818 if (($char == '') || ($char == ' ') || ($char == '\t')) continue; 819 $charVal = ord($char); 820 $nr ^= ((($nr & 63) + $add) * $charVal) + ($nr << 8); 821 $nr2 += ($nr2 << 8) ^ $nr; 822 $add += $charVal; 823 } 824 return sprintf("%08x%08x", ($nr & 0x7fffffff), ($nr2 & 0x7fffffff)); 825 case 'my411': 826 return '*'.sha1(pack("H*", sha1($clear))); 827 default: 828 msg("Unsupported crypt method $method",-1); 829 } 830} 831 832/** 833 * Verifies a cleartext password against a crypted hash 834 * 835 * The method and salt used for the crypted hash is determined automatically 836 * then the clear text password is crypted using the same method. If both hashs 837 * match true is is returned else false 838 * 839 * @author Andreas Gohr <andi@splitbrain.org> 840 * @return bool 841 */ 842function auth_verifyPassword($clear,$crypt){ 843 $method=''; 844 $salt=''; 845 846 //determine the used method and salt 847 $len = strlen($crypt); 848 if(substr($crypt,0,3) == '$1$'){ 849 $method = 'smd5'; 850 $salt = substr($crypt,3,8); 851 }elseif(substr($crypt,0,6) == '{SSHA}'){ 852 $method = 'ssha'; 853 $salt = substr(base64_decode(substr($crypt, 6)),20); 854 }elseif($len == 32){ 855 $method = 'md5'; 856 }elseif($len == 40){ 857 $method = 'sha1'; 858 }elseif($len == 16){ 859 $method = 'mysql'; 860 }elseif($len == 41 && $crypt[0] == '*'){ 861 $method = 'my411'; 862 }else{ 863 $method = 'crypt'; 864 $salt = substr($crypt,0,2); 865 } 866 867 //crypt and compare 868 if(auth_cryptPassword($clear,$method,$salt) === $crypt){ 869 return true; 870 } 871 return false; 872} 873 874//Setup VIM: ex: et ts=2 enc=utf-8 : 875