PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/wiki/inc/auth.php

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