ILIAS  Release_4_4_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 
862  $this->session['registered'] = true;
863  $this->session['username'] = $username;
864  $this->session['timestamp'] = time();
865  $this->session['idle'] = time();
866  }
867 
868  // }}}
869  // {{{ setAdvancedSecurity()
870 
883  function setAdvancedSecurity($flag=true)
884  {
885  $this->advancedsecurity = $flag;
886  }
887 
888  // }}}
889  // {{{ checkAuth()
890 
897  function checkAuth()
898  {
899  $this->log('Auth::checkAuth() called.', AUTH_LOG_DEBUG);
900  $this->authChecks++;
901  if (isset($this->session)) {
902  // Check if authentication session is expired
903  if ( $this->expire > 0
904  && isset($this->session['timestamp'])
905  && ($this->session['timestamp'] + $this->expire) < time()) {
906  $this->log('Session Expired', AUTH_LOG_INFO);
907  $this->expired = true;
908  $this->status = AUTH_EXPIRED;
909  $this->logout();
910  return false;
911  }
912 
913  // Check if maximum idle time is reached
914  if ( $this->idle > 0
915  && isset($this->session['idle'])
916  && ($this->session['idle'] + $this->idle) < time()) {
917  $this->log('Session Idle Time Reached', AUTH_LOG_INFO);
918  $this->idled = true;
919  $this->status = AUTH_IDLED;
920  $this->logout();
921  return false;
922  }
923 
924  if ( isset($this->session['registered'])
925  && isset($this->session['username'])
926  && $this->session['registered'] == true
927  && $this->session['username'] != '') {
929 
930  if ($this->_isAdvancedSecurityEnabled()) {
931  $this->log('Advanced Security Mode Enabled.', AUTH_LOG_DEBUG);
932 
933  // Only Generate the challenge once
934  if ( $this->authChecks == 1
936  $this->log('Generating new Challenge Cookie.', AUTH_LOG_DEBUG);
937  $this->session['challengecookieold'] = $this->session['challengecookie'];
938  $this->session['challengecookie'] = md5($this->session['challengekey'].microtime());
939  setcookie('authchallenge', $this->session['challengecookie'], 0, '/');
940  }
941 
942  // Check for ip change
944  && isset($this->server['REMOTE_ADDR'])
945  && $this->session['sessionip'] != $this->server['REMOTE_ADDR']) {
946  $this->log('Security Breach. Remote IP Address changed.', AUTH_LOG_INFO);
947  // Check if the IP of the user has changed, if so we
948  // assume a man in the middle attack and log him out
949  $this->expired = true;
950  $this->status = AUTH_SECURITY_BREACH;
951  $this->logout();
952  return false;
953  }
954 
955  // Check for ip change (if connected via proxy)
957  && isset($this->server['HTTP_X_FORWARDED_FOR'])
958  && $this->session['sessionforwardedfor'] != $this->server['HTTP_X_FORWARDED_FOR']) {
959  $this->log('Security Breach. Forwarded For IP Address changed.', AUTH_LOG_INFO);
960  // Check if the IP of the user connecting via proxy has
961  // changed, if so we assume a man in the middle attack
962  // and log him out.
963  $this->expired = true;
964  $this->status = AUTH_SECURITY_BREACH;
965  $this->logout();
966  return false;
967  }
968 
969  // Check for useragent change
971  && isset($this->server['HTTP_USER_AGENT'])
972  && $this->session['sessionuseragent'] != $this->server['HTTP_USER_AGENT']) {
973  $this->log('Security Breach. User Agent changed.', AUTH_LOG_INFO);
974  // Check if the User-Agent of the user has changed, if
975  // so we assume a man in the middle attack and log him out
976  $this->expired = true;
977  $this->status = AUTH_SECURITY_BREACH;
978  $this->logout();
979  return false;
980  }
981 
982  // Check challenge cookie here, if challengecookieold is not set
983  // this is the first time and check is skipped
984  // TODO when user open two pages similtaneuly (open in new window,open
985  // in tab) auth breach is caused find out a way around that if possible
987  && isset($this->session['challengecookieold'])
988  && $this->session['challengecookieold'] != $this->cookie['authchallenge']) {
989  $this->log('Security Breach. Challenge Cookie mismatch.', AUTH_LOG_INFO);
990  $this->expired = true;
991  $this->status = AUTH_SECURITY_BREACH;
992  $this->logout();
993  $this->login();
994  return false;
995  }
996  }
997 
998  if (is_callable($this->checkAuthCallback)) {
999  $this->log('Calling checkAuthCallback ('.$this->checkAuthCallback.').', AUTH_LOG_DEBUG);
1000  $checkCallback = call_user_func_array($this->checkAuthCallback, array($this->username, &$this));
1001  if ($checkCallback == false) {
1002  $this->log('checkAuthCallback failed.', AUTH_LOG_INFO);
1003  $this->expired = true;
1004  $this->status = AUTH_CALLBACK_ABORT;
1005  $this->logout();
1006  return false;
1007  }
1008  }
1009 
1010  $this->log('Session OK.', AUTH_LOG_INFO);
1011  return true;
1012  }
1013  } else {
1014  $this->log('Unable to locate session storage.', AUTH_LOG_DEBUG);
1015  return false;
1016  }
1017  $this->log('No login session.', AUTH_LOG_DEBUG);
1018  return false;
1019  }
1020 
1021  // }}}
1022  // {{{ staticCheckAuth() [static]
1023 
1032  function staticCheckAuth($options = null)
1033  {
1034  static $staticAuth;
1035  if(!isset($staticAuth)) {
1036  $staticAuth = new Auth('null', $options);
1037  }
1038  $staticAuth->log('Auth::staticCheckAuth() called', AUTH_LOG_DEBUG);
1039  return $staticAuth->checkAuth();
1040  }
1041 
1042  // }}}
1043  // {{{ getAuth()
1044 
1054  function getAuth()
1055  {
1056  $this->log('Auth::getAuth() called.', AUTH_LOG_DEBUG);
1057  return $this->checkAuth();
1058  }
1059 
1060  // }}}
1061  // {{{ logout()
1062 
1073  function logout()
1074  {
1075  $this->log('Auth::logout() called.', AUTH_LOG_DEBUG);
1076 
1077  if (is_callable($this->logoutCallback) && isset($this->session['username'])) {
1078  $this->log('Calling logoutCallback ('.$this->logoutCallback.').', AUTH_LOG_DEBUG);
1079  call_user_func_array($this->logoutCallback, array($this->session['username'], &$this));
1080  }
1081 
1082  $this->username = '';
1083  $this->password = '';
1084 
1085  $this->session = null;
1086  }
1087 
1088  // }}}
1089  // {{{ updateIdle()
1090 
1097  function updateIdle()
1098  {
1099  $this->session['idle'] = time();
1100  }
1101 
1102  // }}}
1103  // {{{ getUsername()
1104 
1111  function getUsername()
1112  {
1113  if (isset($this->session['username'])) {
1114  return($this->session['username']);
1115  }
1116  return('');
1117  }
1118 
1119  // }}}
1120  // {{{ getStatus()
1121 
1128  function getStatus()
1129  {
1130  return $this->status;
1131  }
1132 
1133  // }}}
1134  // {{{ getPostUsernameField()
1135 
1143  {
1144  return($this->_postUsername);
1145  }
1146 
1147  // }}}
1148  // {{{ getPostPasswordField()
1149 
1157  {
1158  return($this->_postPassword);
1159  }
1160 
1161  // }}}
1162  // {{{ sessionValidThru()
1163 
1170  function sessionValidThru()
1171  {
1172  if (!isset($this->session['idle'])) {
1173  return 0;
1174  }
1175  if ($this->idle == 0) {
1176  return 0;
1177  }
1178  return ($this->session['idle'] + $this->idle);
1179  }
1180 
1181  // }}}
1182  // {{{ listUsers()
1183 
1191  function listUsers()
1192  {
1193  $this->log('Auth::listUsers() called.', AUTH_LOG_DEBUG);
1194  $this->_loadStorage();
1195  return $this->storage->listUsers();
1196  }
1197 
1198  // }}}
1199  // {{{ addUser()
1200 
1212  {
1213  $this->log('Auth::addUser() called.', AUTH_LOG_DEBUG);
1214  $this->_loadStorage();
1215  return $this->storage->addUser($username, $password, $additional);
1216  }
1217 
1218  // }}}
1219  // {{{ removeUser()
1220 
1230  {
1231  $this->log('Auth::removeUser() called.', AUTH_LOG_DEBUG);
1232  $this->_loadStorage();
1233  return $this->storage->removeUser($username);
1234  }
1235 
1236  // }}}
1237  // {{{ changePassword()
1238 
1249  {
1250  $this->log('Auth::changePassword() called', AUTH_LOG_DEBUG);
1251  $this->_loadStorage();
1252  return $this->storage->changePassword($username, $password);
1253  }
1254 
1255  // }}}
1256  // {{{ log()
1257 
1266  function log($message, $level = AUTH_LOG_DEBUG)
1267  {
1268  if (!$this->enableLogging) return false;
1269 
1270  $this->_loadLogger();
1271 
1272  $this->logger->log('AUTH: '.$message, $level);
1273  }
1274 
1275  // }}}
1276  // {{{ _loadLogger()
1277 
1288  function _loadLogger()
1289  {
1290  if(is_null($this->logger)) {
1291  if (!class_exists('Log')) {
1292  include_once 'Log.php';
1293  }
1294  $this->logger =& Log::singleton('null',
1295  null,
1296  'auth['.getmypid().']',
1297  array(),
1298  AUTH_LOG_DEBUG);
1299  return(true);
1300  }
1301  return(false);
1302  }
1303 
1304  // }}}
1305  // {{{ attachLogObserver()
1306 
1313  function attachLogObserver(&$observer) {
1314 
1315  $this->_loadLogger();
1316 
1317  return $this->logger->attach($observer);
1318 
1319  }
1320 
1321  // }}}
1322  // {{{ _isAdvancedSecurityEnabled()
1323 
1333  function _isAdvancedSecurityEnabled($feature = null) {
1334 
1335  if (is_null($feature)) {
1336 
1337  if ($this->advancedsecurity === true)
1338  return true;
1339 
1340  if ( is_array($this->advancedsecurity)
1341  && in_array(true, $this->advancedsecurity, true))
1342  return true;
1343 
1344  return false;
1345 
1346  } else {
1347 
1348  if (is_array($this->advancedsecurity)) {
1349 
1350  if ( isset($this->advancedsecurity[$feature])
1351  && $this->advancedsecurity[$feature] == true)
1352  return true;
1353 
1354  return false;
1355 
1356  }
1357 
1358  return (bool)$this->advancedsecurity;
1359 
1360  }
1361 
1362  }
1363 
1364  // }}}
1365 
1366 }
1367 ?>