diff --git a/html/secondfactor.php b/html/secondfactor.php new file mode 100644 index 0000000000000000000000000000000000000000..5b6f833d6a59800fe4387e19cf0dd6d0244183dd --- /dev/null +++ b/html/secondfactor.php @@ -0,0 +1,103 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003-2010 Cajus Pollmeier + Copyright (C) 2011-2019 FusionDirectory + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* Basic setup, remove eventually registered sessions */ +require_once("../include/php_setup.inc"); +require_once("functions.inc"); +require_once("variables.inc"); + +/* Set headers */ +header('Content-type: text/html; charset=UTF-8'); +header('X-XSS-Protection: 1; mode=block'); +header('X-Content-Type-Options: nosniff'); +header('X-Frame-Options: deny'); + +/* Set the text domain as 'fusiondirectory' */ +$domain = 'fusiondirectory'; +bindtextdomain($domain, LOCALE_DIR); +textdomain($domain); + +/* Remember everything we did after the last click */ +session::start(); +session::set('DEBUGLEVEL', 0); +reset_errors(); + +/* Force SSL for second factor */ +if ($ssl != '') { + header("Location: $ssl"); + exit; +} + +CSRFProtection::check(); + +/* Logged in? Redirect to FD */ +if (session::is_set('connected')) { + header('Location: main.php'); + exit; +} + +/* Missing data? Redirect to login */ +if (!session::is_set('ui') || !session::is_set('config')) { + header('Location: index.php'); + exit; +} + +$ui = session::get('ui'); +$config = session::get('config'); + +timezone::setDefaultTimezoneFromConfig(); + +/* Check for invalid sessions */ +if (session::get('_LAST_PAGE_REQUEST') != '') { + /* check FusionDirectory.conf for defined session lifetime */ + $max_life = $config->get_cfg_value('sessionLifetime', 60 * 60 * 2); + + if ($max_life > 0) { + /* get time difference between last page reload */ + $request_time = (time() - session::get('_LAST_PAGE_REQUEST')); + + /* If page wasn't reloaded for more than max_life seconds + * kill session + */ + if ($request_time > $max_life) { + session::destroy('main.php called with expired session'); + header('Location: index.php?signout=1&message=expired'); + exit; + } + } +} +session::set('_LAST_PAGE_REQUEST', time()); + +foreach (LoginPost::$secondFactorMethods as $secondFactorMethod) { + if (!class_available($secondFactorMethod)) { + continue; + } + $secondFactorMethod::earlyProcess(); +} + +session::set('DEBUGLEVEL', $config->get_cfg_value('DEBUGLEVEL')); + +/* Set template compile directory */ +$smarty->compile_dir = $config->get_cfg_value('templateCompileDirectory', SPOOL_DIR); + +Language::init(); + +LoginPost::displaySecondFactorPage(); diff --git a/ihtml/themes/breezy/secondfactor.tpl b/ihtml/themes/breezy/secondfactor.tpl new file mode 100644 index 0000000000000000000000000000000000000000..8dac1d49fa3c3696d1a3706463a4cc136acc9ecf --- /dev/null +++ b/ihtml/themes/breezy/secondfactor.tpl @@ -0,0 +1,48 @@ +<body> + + {$php_errors} + +{* FusionDirectory login - smarty template *} + +<div id="window-container"> + +<div id="window-div"> +<form action="index.php" method="post" id="loginform" name="loginform"> + +{$msg_dialogs} + <div id="window-titlebar"> + <img id="fd-logo" src="geticon.php?context=applications&icon=fusiondirectory&size=48" alt="FusionDirectory logo"/> + <p> + {t}Two factor authentication{/t} + </p> + </div> + <div id="window-content"> + {foreach from=$methodOutputs key=method item=methodOutput} + <div class="secondfactormethod" id="{$method|escape}"> + {$methodOutput} + </div> + {/foreach} + </div> + <div id="window-footer" class="plugbottom"> + <div> + <!-- Display error message on demand --> + {$message} + </div> + <div> + </div> + </div> + +</form> +</div> + +</div> + +{include file={filePath file="copynotice.tpl"}} + +<script type="text/javascript"> +<!-- + next_msg_dialog(); +--> +</script> +</body> +</html> diff --git a/include/login/class_LoginMethod.inc b/include/login/class_LoginMethod.inc index ac07f9d3ee61fb63b7f570e4795e3b8cfc7f9d66..9f4f83bdd1f4502dc01f7f0e01cd59d93e4d476d 100644 --- a/include/login/class_LoginMethod.inc +++ b/include/login/class_LoginMethod.inc @@ -145,15 +145,23 @@ class LoginMethod return TRUE; } - /*! \brief Final step of successful login: redirect to main.php */ - static function redirect () + /*! \brief Connect user */ + static function connect () { global $config, $ui; + $ui = session::get('ui'); + /* Not account expired or password forced change go to main page */ - logging::log('security', 'login', static::$username, [], 'Logged in successfully'); + logging::log('security', 'login', $ui->uid, [], 'Logged in successfully'); session::set('connected', 1); session::set('DEBUGLEVEL', $config->get_cfg_value('DEBUGLEVEL')); + } + + /*! \brief Final step of successful login: redirect to main.php */ + static function redirect () + { + static::connect(); header('Location: main.php'); exit; } diff --git a/include/login/class_LoginPost.inc b/include/login/class_LoginPost.inc index 37465f574faef08b604326dedd732031686b1c43..c68f5ddbbcaea41ee99ec0b242824893a244f292 100644 --- a/include/login/class_LoginPost.inc +++ b/include/login/class_LoginPost.inc @@ -23,12 +23,28 @@ */ class LoginPost extends LoginMethod { + /*! \brief List of second factor methods, may be dynamic later */ + static $secondFactorMethods = ['SecondFactorWebAuthn']; + /*! \brief Displayed name */ static function getLabel () { return _('HTML form'); } + static function init () + { + parent::init(); + + /* Init second factor methods if needed */ + foreach (static::$secondFactorMethods as $secondFactorMethod) { + if (!class_available($secondFactorMethod)) { + continue; + } + $secondFactorMethod::init(); + } + } + /*! \brief All login steps in the right order for standard POST login */ static function loginProcess () { @@ -50,6 +66,16 @@ class LoginPost extends LoginMethod 'runSchemaCheck', ]); + /* If needed redirect to second factor page */ + foreach (static::$secondFactorMethods as $secondFactorMethod) { + if (!class_available($secondFactorMethod)) { + continue; + } + if ($secondFactorMethod::hasSecondFactor()) { + static::redirectSecondFactorPage(); + } + } + if ($success) { /* Everything went well, redirect to main.php */ static::redirect(); @@ -62,6 +88,14 @@ class LoginPost extends LoginMethod static::displayLogin(); } + /*! \brief Redirect to the second factor page */ + static protected function redirectSecondFactorPage () + { + session::un_set('connected'); + header('Location: secondfactor.php'); + exit; + } + /*! \brief Display the login page and exit() */ static protected function displayLogin () { @@ -134,4 +168,66 @@ class LoginPost extends LoginMethod $smarty->display(get_template_path('login.tpl')); exit(); } + + /*! \brief Display the second factor page and exit() */ + static function displaySecondFactorPage () + { + global $smarty,$message,$config,$ssl,$error_collector,$error_collector_mailto; + + $lang = session::get('lang'); + + error_reporting(E_ALL | E_STRICT); + + /* Fill template with required values */ + $username = ''; + if (isset($_POST['username'])) { + $username = trim($_POST['username']); + } + $smarty->assign('date', gmdate('D, d M Y H:i:s')); + $smarty->assign('username', $username); + $smarty->assign('revision', FD_VERSION); + $smarty->assign('year', date('Y')); + $smarty->append('css_files', get_template_path('login.css')); + $smarty->assign('title', _('Second factor')); + + /* Some error to display? */ + if (!isset($message)) { + $message = ''; + } + $smarty->assign('message', $message); + + /* show login screen */ + $smarty->assign('PHPSESSID', session_id()); + if ($error_collector != '') { + $smarty->assign('php_errors', preg_replace('/%BUGBODY%/', $error_collector_mailto, $error_collector).'</div>'); + } else { + $smarty->assign('php_errors', ''); + } + $smarty->assign('msg_dialogs', msg_dialog::get_dialogs()); + $smarty->assign('usePrototype', 'false'); + $smarty->assign('date', date('l, dS F Y H:i:s O')); + $smarty->assign('lang', preg_replace('/_.*$/', '', $lang)); + $smarty->assign('rtl', Language::isRTL($lang)); + + $methodOutputs = []; + + /* Run second factor methods */ + foreach (static::$secondFactorMethods as $secondFactorMethod) { + if (!class_available($secondFactorMethod)) { + continue; + } + $methodOutput = $secondFactorMethod::execute(); + if ($methodOutput !== NULL) { + $methodOutputs[$secondFactorMethod] = $methodOutput; + } + } + + $smarty->assign('methodOutputs', $methodOutputs); + + $smarty->display(get_template_path('headers.tpl')); + $smarty->assign('version', FD_VERSION); + + $smarty->display(get_template_path('secondfactor.tpl')); + exit(); + } }