ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
Auth.php
Go to the documentation of this file.
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3 
4 include_once './Services/Authentication/classes/class.ilAuthBase.php';
5 
30 define('AUTH_IDLED', -1);
34 define('AUTH_EXPIRED', -2);
38 define('AUTH_WRONG_LOGIN', -3);
42 define('AUTH_METHOD_NOT_SUPPORTED', -4);
46 define('AUTH_SECURITY_BREACH', -5);
50 define('AUTH_CALLBACK_ABORT', -6);
51 
55 define('AUTH_LOG_INFO', 6);
59 define('AUTH_LOG_DEBUG', 7);
60 
64 define('AUTH_ADV_IPCHECK', 1);
68 define('AUTH_ADV_USERAGENT', 2);
72 define('AUTH_ADV_CHALLENGE', 3);
73 
74 
90 class Auth extends ilAuthBase {
91 
92  // {{{ properties
93 
102  var $expire = 0;
103 
110  var $expired = false;
111 
122  var $idle = 0;
123 
130  var $idled = false;
131 
138  var $storage = '';
139 
145  var $loginFunction = '';
146 
153  var $showLogin = true;
154 
161  var $allowLogin = true;
162 
168  var $status = '';
169 
175  var $username = '';
176 
182  var $password = '';
183 
191 
198  var $loginCallback = '';
199 
207 
214  var $logoutCallback = '';
215 
221  var $_sessionName = '_authsession';
222 
228  var $version = "@version@";
229 
245  var $advancedsecurity = false;
246 
252  var $_postUsername = 'username';
253 
259  var $_postPassword = 'password';
260 
265  var $session;
266 
271  var $server;
272 
277  var $post;
278 
283  var $cookie;
284 
290 
295  var $authChecks = 0;
296 
302  var $logger = null;
303 
309  var $enableLogging = false;
310 
316  var $regenerateSessionId = false;
317 
318  // }}}
319  // {{{ Auth() [constructor]
320 
335  function Auth($storageDriver, $options = '', $loginFunction = '', $showLogin = true)
336  {
337  $this->applyAuthOptions($options);
338 
339  // Start the session suppress error if already started
340  if(!session_id()){
341  @session_start();
342  if(!session_id()) {
343  // Throw error
344  include_once 'PEAR.php';
345  PEAR::throwError('Session could not be started by Auth, '
346  .'possibly headers are already sent, try putting '
347  .'ob_start in the beginning of your script');
348  }
349  }
350 
351  // Make Sure Auth session variable is there
352  if(!isset($_SESSION[$this->_sessionName])) {
353  $_SESSION[$this->_sessionName] = array();
354  }
355 
356  // Assign Some globals to internal references, this will replace _importGlobalVariable
357  $this->session =& $_SESSION[$this->_sessionName];
358  $this->server =& $_SERVER;
359  $this->post =& $_POST;
360  $this->cookie =& $_COOKIE;
361 
362  if ($loginFunction != '' && is_callable($loginFunction)) {
363  $this->loginFunction = $loginFunction;
364  }
365 
366  if (is_bool($showLogin)) {
367  $this->showLogin = $showLogin;
368  }
369 
370  if (is_object($storageDriver)) {
371  $this->storage =& $storageDriver;
372  // Pass a reference to auth to the container, ugly but works
373  // this is used by the DB container to use method setAuthData not staticaly.
374  $this->storage->_auth_obj =& $this;
375  } else {
376  // $this->storage = $this->_factory($storageDriver, $options);
377  //
378  $this->storage_driver = $storageDriver;
379  $this->storage_options =& $options;
380  }
381  }
382 
383  // }}}
384  // {{{ applyAuthOptions()
385 
397  {
398  if(is_array($options)){
399  if (!empty($options['sessionName'])) {
400  $this->_sessionName = $options['sessionName'];
401  unset($options['sessionName']);
402  }
403  if (isset($options['allowLogin'])) {
404  $this->allowLogin = $options['allowLogin'];
405  unset($options['allowLogin']);
406  }
407  if (!empty($options['postUsername'])) {
408  $this->_postUsername = $options['postUsername'];
409  unset($options['postUsername']);
410  }
411  if (!empty($options['postPassword'])) {
412  $this->_postPassword = $options['postPassword'];
413  unset($options['postPassword']);
414  }
415  if (isset($options['advancedsecurity'])) {
416  $this->advancedsecurity = $options['advancedsecurity'];
417  unset($options['advancedsecurity']);
418  }
419  if (isset($options['enableLogging'])) {
420  $this->enableLogging = $options['enableLogging'];
421  unset($options['enableLogging']);
422  }
423  if (isset($options['regenerateSessionId']) && is_bool($options['regenerateSessionId'])) {
424  $this->regenerateSessionId = $options['regenerateSessionId'];
425  }
426  }
427  return($options);
428  }
429 
430  // }}}
431  // {{{ _loadStorage()
432 
443  function _loadStorage()
444  {
445  if(!is_object($this->storage)) {
446  $this->storage =& $this->_factory($this->storage_driver,
447  $this->storage_options);
448  $this->storage->_auth_obj =& $this;
449  $this->log('Loaded storage container ('.$this->storage_driver.')', AUTH_LOG_DEBUG);
450  return(true);
451  }
452  return(false);
453  }
454 
455  // }}}
456  // {{{ _factory()
457 
467  function &_factory($driver, $options = '')
468  {
469  $storage_class = 'Auth_Container_' . $driver;
470  include_once 'Auth/Container/' . $driver . '.php';
471  $obj =& new $storage_class($options);
472  return $obj;
473  }
474 
475  // }}}
476  // {{{ assignData()
477 
491  function assignData()
492  {
493  $this->log('Auth::assignData() called.', AUTH_LOG_DEBUG);
494 
495  if ( isset($this->post[$this->_postUsername])
496  && $this->post[$this->_postUsername] != '') {
497  $this->username = (get_magic_quotes_gpc() == 1
498  ? stripslashes($this->post[$this->_postUsername])
499  : $this->post[$this->_postUsername]);
500  }
501  if ( isset($this->post[$this->_postPassword])
502  && $this->post[$this->_postPassword] != '') {
503  $this->password = (get_magic_quotes_gpc() == 1
504  ? stripslashes($this->post[$this->_postPassword])
505  : $this->post[$this->_postPassword] );
506  }
507  }
508 
509  // }}}
510  // {{{ start()
511 
518  function start()
519  {
520  $this->log('Auth::start() called.', AUTH_LOG_DEBUG);
521 
522  // #10729 - Regenerate session id here if we are generating it on every
523  // page load.
524  if ($this->regenerateSessionId) {
525  session_regenerate_id(true);
526  }
527 
528  $this->assignData();
529  if (!$this->checkAuth() && $this->allowLogin) {
530  $this->login();
531  }
532  }
533 
534  // }}}
535  // {{{ login()
536 
543  function login()
544  {
545  $this->log('Auth::login() called.', AUTH_LOG_DEBUG);
546 
547  $login_ok = false;
548  $this->_loadStorage();
549 
550  // Check if using challenge response
551  (isset($this->post['authsecret']) && $this->post['authsecret'] == 1)
552  ? $usingChap = true
553  : $usingChap = false;
554 
555 
556  // When the user has already entered a username, we have to validate it.
557  if (!empty($this->username)) {
558  if (true === $this->storage->fetchData($this->username, $this->password, $usingChap)) {
559  $this->session['challengekey'] = md5($this->username.$this->password);
560  $login_ok = true;
561  $this->log('Successful login.', AUTH_LOG_INFO);
562  }
563  }
564 
565  if (!empty($this->username) && $login_ok) {
566  $this->setAuth($this->username);
567  if (is_callable($this->loginCallback)) {
568  $this->log('Calling loginCallback ('.$this->loginCallback.').', AUTH_LOG_DEBUG);
569  call_user_func_array($this->loginCallback, array($this->username, &$this));
570  }
571  }
572 
573  // If the login failed or the user entered no username,
574  // output the login screen again.
575  if (!empty($this->username) && !$login_ok) {
576  $this->log('Incorrect login.', AUTH_LOG_INFO);
577  $this->status = AUTH_WRONG_LOGIN;
578  if (is_callable($this->loginFailedCallback)) {
579  $this->log('Calling loginFailedCallback ('.$this->loginFailedCallback.').', AUTH_LOG_DEBUG);
580  call_user_func_array($this->loginFailedCallback, array($this->username, &$this));
581  }
582  }
583 
584  if ((empty($this->username) || !$login_ok) && $this->showLogin) {
585  $this->log('Rendering Login Form.', AUTH_LOG_INFO);
586  if (is_callable($this->loginFunction)) {
587  $this->log('Calling loginFunction ('.$this->loginFunction.').', AUTH_LOG_DEBUG);
588  call_user_func_array($this->loginFunction, array($this->username, $this->status, &$this));
589  } else {
590  // BC fix Auth used to use drawLogin for this
591  // call is sub classes implement this
592  if (is_callable(array($this, 'drawLogin'))) {
593  $this->log('Calling Auth::drawLogin()', AUTH_LOG_DEBUG);
594  return $this->drawLogin($this->username, $this);
595  }
596 
597  $this->log('Using default Auth_Frontend_Html', AUTH_LOG_DEBUG);
598 
599  // New Login form
600  include_once 'Auth/Frontend/Html.php';
601  return Auth_Frontend_Html::render($this, $this->username);
602  }
603  } else {
604  return;
605  }
606  }
607 
608  // }}}
609  // {{{ setExpire()
610 
619  function setExpire($time, $add = false)
620  {
621  $add ? $this->expire += $time : $this->expire = $time;
622  }
623 
624  // }}}
625  // {{{ setIdle()
626 
635  function setIdle($time, $add = false)
636  {
637  $add ? $this->idle += $time : $this->idle = $time;
638  }
639 
640  // }}}
641  // {{{ setSessionName()
642 
656  function setSessionName($name = 'session')
657  {
658  $this->_sessionName = '_auth_'.$name;
659  // Make Sure Auth session variable is there
660  if(!isset($_SESSION[$this->_sessionName])) {
661  $_SESSION[$this->_sessionName] = array();
662  }
663  $this->session =& $_SESSION[$this->_sessionName];
664  }
665 
666  // }}}
667  // {{{ setShowLogin()
668 
676  function setShowLogin($showLogin = true)
677  {
678  $this->showLogin = $showLogin;
679  }
680 
681  // }}}
682  // {{{ setAllowLogin()
683 
691  function setAllowLogin($allowLogin = true)
692  {
693  $this->allowLogin = $allowLogin;
694  }
695 
696  // }}}
697  // {{{ setCheckAuthCallback()
698 
709  {
710  $this->checkAuthCallback = $checkAuthCallback;
711  }
712 
713  // }}}
714  // {{{ setLoginCallback()
715 
726  {
727  $this->loginCallback = $loginCallback;
728  }
729 
730  // }}}
731  // {{{ setFailedLoginCallback()
732 
742  {
743  $this->loginFailedCallback = $loginFailedCallback;
744  }
745 
746  // }}}
747  // {{{ setLogoutCallback()
748 
759  {
760  $this->logoutCallback = $logoutCallback;
761  }
762 
763  // }}}
764  // {{{ setAuthData()
765 
777  function setAuthData($name, $value, $overwrite = true)
778  {
779  if (!empty($this->session['data'][$name]) && $overwrite == false) {
780  return;
781  }
782  $this->session['data'][$name] = $value;
783  }
784 
785  // }}}
786  // {{{ getAuthData()
787 
798  function getAuthData($name = null)
799  {
800  if (!isset($this->session['data'])) {
801  return null;
802  }
803  if(!isset($name)) {
804  return $this->session['data'];
805  }
806  if (isset($name) && isset($this->session['data'][$name])) {
807  return $this->session['data'][$name];
808  }
809  return null;
810  }
811 
812  // }}}
813  // {{{ setAuth()
814 
823  function setAuth($username)
824  {
825  $this->log('Auth::setAuth() called.', AUTH_LOG_DEBUG);
826 
827  // #10729 - Regenerate session id here only if generating at login only
828  // Don't do it if we are regenerating on every request so we don't
829  // regenerate it twice in one request.
830  if (!$this->regenerateSessionId) {
831  // #2021 - Change the session id to avoid session fixation attacks php 4.3.3 >
832  session_regenerate_id(true);
833  }
834 
835  if (!isset($this->session) || !is_array($this->session)) {
836  $this->session = array();
837  }
838 
839  if (!isset($this->session['data'])) {
840  $this->session['data'] = array();
841  }
842 
843  $this->session['sessionip'] = isset($this->server['REMOTE_ADDR'])
844  ? $this->server['REMOTE_ADDR']
845  : '';
846  $this->session['sessionuseragent'] = isset($this->server['HTTP_USER_AGENT'])
847  ? $this->server['HTTP_USER_AGENT']
848  : '';
849  $this->session['sessionforwardedfor'] = isset($this->server['HTTP_X_FORWARDED_FOR'])
850  ? $this->server['HTTP_X_FORWARDED_FOR']
851  : '';
852 
853  // This should be set by the container to something more safe
854  // Like md5(passwd.microtime)
855  if(empty($this->session['challengekey'])) {
856  $this->session['challengekey'] = md5($username.microtime());
857  }
858 
859  $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
860  //setcookie('authchallenge', $this->session['challengecookie'], 0, '/');
861  require_once 'Services/Utilities/classes/class.ilUtil.php';
862  ilUtil::setCookie('authchallenge', $this->session['challengecookie']);
863 
864  $this->session['registered'] = true;
865  $this->session['username'] = $username;
866  $this->session['timestamp'] = time();
867  $this->session['idle'] = time();
868  }
869 
870  // }}}
871  // {{{ setAdvancedSecurity()
872 
885  function setAdvancedSecurity($flag=true)
886  {
887  $this->advancedsecurity = $flag;
888  }
889 
890  // }}}
891  // {{{ checkAuth()
892 
899  function checkAuth()
900  {
901  $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG);
902  $this->authChecks++;
903  if (isset($this->session)) {
904  // Check if authentication session is expired
905  if ( $this->expire > 0
906  && isset($this->session['timestamp'])
907  && ($this->session['timestamp'] + $this->expire) < time()) {
908  $this->log('Session Expired', AUTH_LOG_INFO);
909  $this->expired = true;
910  $this->status = AUTH_EXPIRED;
911  $this->logout();
912  return false;
913  }
914 
915  // Check if maximum idle time is reached
916  if ( $this->idle > 0
917  && isset($this->session['idle'])
918  && ($this->session['idle'] + $this->idle) < time()) {
919  $this->log('Session Idle Time Reached', AUTH_LOG_INFO);
920  $this->idled = true;
921  $this->status = AUTH_IDLED;
922  $this->logout();
923  return false;
924  }
925 
926  if ( isset($this->session['registered'])
927  && isset($this->session['username'])
928  && $this->session['registered'] == true
929  && $this->session['username'] != '') {
931 
932  if ($this->_isAdvancedSecurityEnabled()) {
933  $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG);
934 
935  // Only Generate the challenge once
936  if ( $this->authChecks == 1
938  $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG);
939  $this->session['challengecookieold'] = $this->session['challengecookie'];
940  $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
941  //setcookie('authchallenge', $this->session['challengecookie'], 0, '/');
942  require_once 'Services/Utilities/classes/class.ilUtil.php';
943  ilUtil::setCookie('authchallenge', $this->session['challengecookie']);
944  }
945 
946  // Check for ip change
948  && isset($this->server['REMOTE_ADDR'])
949  && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
950  $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO);
951  // Check if the IP of the user has changed, if so we
952  // assume a man in the middle attack and log him out
953  $this->expired = true;
954  $this->status = AUTH_SECURITY_BREACH;
955  $this->logout();
956  return false;
957  }
958 
959  // Check for ip change (if connected via proxy)
961  && isset($this->server['HTTP_X_FORWARDED_FOR'])
962  && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) {
963  $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO);
964  // Check if the IP of the user connecting via proxy has
965  // changed, if so we assume a man in the middle attack
966  // and log him out.
967  $this->expired = true;
968  $this->status = AUTH_SECURITY_BREACH;
969  $this->logout();
970  return false;
971  }
972 
973  // Check for useragent change
975  && isset($this->server['HTTP_USER_AGENT'])
976  && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
977  $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO);
978  // Check if the User-Agent of the user has changed, if
979  // so we assume a man in the middle attack and log him out
980  $this->expired = true;
981  $this->status = AUTH_SECURITY_BREACH;
982  $this->logout();
983  return false;
984  }
985 
986  // Check challenge cookie here, if challengecookieold is not set
987  // this is the first time and check is skipped
988  // TODO when user open two pages similtaneuly (open in new window,open
989  // in tab) auth breach is caused find out a way around that if possible
991  && isset($this->session['challengecookieold'])
992  && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
993  $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO);
994  $this->expired = true;
995  $this->status = AUTH_SECURITY_BREACH;
996  $this->logout();
997  $this->login();
998  return false;
999  }
1000  }
1001 
1002  if (is_callable($this->checkAuthCallback)) {
1003  $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG);
1004  $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this));
1005  if ($checkCallback == false) {
1006  $this->log('checkAuthCallback failed.', AUTH_LOG_INFO);
1007  $this->expired = true;
1008  $this->status = AUTH_CALLBACK_ABORT;
1009  $this->logout();
1010  return false;
1011  }
1012  }
1013 
1014  $this->log('Session OK.', AUTH_LOG_INFO);
1015  return true;
1016  }
1017  } else {
1018  $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG);
1019  return false;
1020  }
1021  $this->log('No login session.', AUTH_LOG_DEBUG);
1022  return false;
1023  }
1024 
1025  // }}}
1026  // {{{ staticCheckAuth() [static]
1027 
1036  function staticCheckAuth($options = null)
1037  {
1038  static $staticAuth;
1039  if(!isset($staticAuth)) {
1040  $staticAuth = new Auth('null', $options);
1041  }
1042  $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG);
1043  return $staticAuth->checkAuth();
1044  }
1045 
1046  // }}}
1047  // {{{ getAuth()
1048 
1058  function getAuth()
1059  {
1060  $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG);
1061  return $this->checkAuth();
1062  }
1063 
1064  // }}}
1065  // {{{ logout()
1066 
1077  function logout()
1078  {
1079  $this->log('Auth::logout() called.', AUTH_LOG_DEBUG);
1080 
1081  if (is_callable($this->logoutCallback) && isset($this->session['username'])) {
1082  $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG);
1083  call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
1084  }
1085 
1086  $this->username = '';
1087  $this->password = '';
1088 
1089  $this->session = null;
1090  }
1091 
1092  // }}}
1093  // {{{ updateIdle()
1094 
1101  function updateIdle()
1102  {
1103  $this->session['idle'] = time();
1104  }
1105 
1106  // }}}
1107  // {{{ getUsername()
1108 
1115  function getUsername()
1116  {
1117  if (isset($this->session['username'])) {
1118  return($this->session['username']);
1119  }
1120  return('');
1121  }
1122 
1123  // }}}
1124  // {{{ getStatus()
1125 
1132  function getStatus()
1133  {
1134  return $this->status;
1135  }
1136 
1137  // }}}
1138  // {{{ getPostUsernameField()
1139 
1147  {
1148  return($this->_postUsername);
1149  }
1150 
1151  // }}}
1152  // {{{ getPostPasswordField()
1153 
1161  {
1162  return($this->_postPassword);
1163  }
1164 
1165  // }}}
1166  // {{{ sessionValidThru()
1167 
1174  function sessionValidThru()
1175  {
1176  if (!isset($this->session['idle'])) {
1177  return 0;
1178  }
1179  if ($this->idle == 0) {
1180  return 0;
1181  }
1182  return ($this->session['idle'] + $this->idle);
1183  }
1184 
1185  // }}}
1186  // {{{ listUsers()
1187 
1195  function listUsers()
1196  {
1197  $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG);
1198  $this->_loadStorage();
1199  return $this->storage->listUsers();
1200  }
1201 
1202  // }}}
1203  // {{{ addUser()
1204 
1216  {
1217  $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG);
1218  $this->_loadStorage();
1219  return $this->storage->addUser($username, $password, $additional);
1220  }
1221 
1222  // }}}
1223  // {{{ removeUser()
1224 
1234  {
1235  $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG);
1236  $this->_loadStorage();
1237  return $this->storage->removeUser($username);
1238  }
1239 
1240  // }}}
1241  // {{{ changePassword()
1242 
1253  {
1254  $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG);
1255  $this->_loadStorage();
1256  return $this->storage->changePassword($username, $password);
1257  }
1258 
1259  // }}}
1260  // {{{ log()
1261 
1270  function log($message, $level = AUTH_LOG_DEBUG)
1271  {
1272  if (!$this->enableLogging) return false;
1273 
1274  $this->_loadLogger();
1275 
1276  $this->logger->log('AUTH: '.$message, $level);
1277  }
1278 
1279  // }}}
1280  // {{{ _loadLogger()
1281 
1292  function _loadLogger()
1293  {
1294  if(is_null($this->logger)) {
1295  if (!class_exists('Log')) {
1296  include_once 'Log.php';
1297  }
1298  $this->logger =& Log::singleton('null',
1299  null,
1300  'auth['.getmypid().']',
1301  array(),
1302  AUTH_LOG_DEBUG);
1303  return(true);
1304  }
1305  return(false);
1306  }
1307 
1308  // }}}
1309  // {{{ attachLogObserver()
1310 
1317  function attachLogObserver(&$observer) {
1318 
1319  $this->_loadLogger();
1320 
1321  return $this->logger->attach($observer);
1322 
1323  }
1324 
1325  // }}}
1326  // {{{ _isAdvancedSecurityEnabled()
1327 
1337  function _isAdvancedSecurityEnabled($feature = null) {
1338 
1339  if (is_null($feature)) {
1340 
1341  if ($this->advancedsecurity === true)
1342  return true;
1343 
1344  if ( is_array($this->advancedsecurity)
1345  && in_array(true, $this->advancedsecurity, true))
1346  return true;
1347 
1348  return false;
1349 
1350  } else {
1351 
1352  if (is_array($this->advancedsecurity)) {
1353 
1354  if ( isset($this->advancedsecurity[$feature])
1355  && $this->advancedsecurity[$feature] == true)
1356  return true;
1357 
1358  return false;
1359 
1360  }
1361 
1362  return (bool)$this->advancedsecurity;
1363 
1364  }
1365 
1366  }
1367 
1368  // }}}
1369 
1370 }
1371 ?>