2 /**********************************************************************
3 Copyright (C) FrontAccounting, LLC.
4 Released under the terms of the GNU General Public License, GPL,
5 as published by the Free Software Foundation, either version 3
6 of the License, or (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
15 function sessionStart($name, $limit = 0, $path = '/', $domain = null, $secure = null)
17 // Set the cookie name
21 $https = isset($secure) ? $secure : (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
23 // Set session cookie options
24 session_set_cookie_params($limit, $path, $domain, $https, true);
27 // Make sure the session hasn't expired, and destroy it if it has
28 if ($this->validateSession())
30 // Check to see if the session is new or a hijacking attempt
31 if(!$this->preventHijacking())
33 // Reset session data and regenerate id
35 $_SESSION['IPaddress'] = $_SERVER['REMOTE_ADDR'];
36 $_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
37 $this->regenerateSession();
39 // Give a 5% chance of the session id changing on any request
41 elseif (rand(1, 100) <= 5)
43 $this->regenerateSession();
54 function preventHijacking()
56 if (!isset($_SESSION['IPaddress']) || !isset($_SESSION['userAgent']))
59 if ($_SESSION['IPaddress'] != $_SERVER['REMOTE_ADDR'])
62 if ( $_SESSION['userAgent'] != $_SERVER['HTTP_USER_AGENT'])
68 function regenerateSession()
70 // If this session is obsolete it means there already is a new id
71 if (isset($_SESSION['OBSOLETE']) && ($_SESSION['OBSOLETE'] == true))
74 // Set current session to expire in 10 seconds
75 $_SESSION['OBSOLETE'] = true;
76 $_SESSION['EXPIRES'] = time() + 10;
78 // Create new session without destroying the old one
79 session_regenerate_id();
81 // Grab current session ID and close both sessions to allow other scripts to use them
82 $newSession = session_id();
83 session_write_close();
84 // Set session ID to the new one, and start it back up again
86 session_id($newSession);
89 // Now we unset the obsolete and expiration values for the session we want to keep
90 unset($_SESSION['OBSOLETE']);
91 unset($_SESSION['EXPIRES']);
94 function validateSession()
96 if (isset($_SESSION['OBSOLETE']) && !isset($_SESSION['EXPIRES']) )
99 if (isset($_SESSION['EXPIRES']) && $_SESSION['EXPIRES'] < time())
106 function output_html($text)
108 global $before_box, $Ajax, $messages;
109 // Fatal errors are not send to error_handler,
110 // so we must check the output
111 if ($text && preg_match('/\bFatal error(<.*?>)?:(.*)/i', $text, $m)) {
112 $Ajax->aCommands = array(); // Don't update page via ajax on errors
113 $text = preg_replace('/\bFatal error(<.*?>)?:(.*)/i','', $text);
114 $messages[] = array(E_ERROR, $m[2], null, null);
117 return in_ajax() ? fmt_errors() : ($before_box.fmt_errors().$text);
119 //----------------------------------------------------------------------------------------
121 function kill_login()
126 //----------------------------------------------------------------------------------------
128 function login_fail()
130 global $path_to_root;
132 header("HTTP/1.1 401 Authorization Required");
133 echo "<center><br><br><font size='5' color='red'><b>" . _("Incorrect Password") . "<b></font><br><br>";
134 echo "<b>" . _("The user and password combination is not valid for the system.") . "<b><br><br>";
136 echo _("If you are not an authorized user, please contact your system administrator to obtain an account to enable you to use the system.");
137 echo "<br><a href='$path_to_root/index.php'>" . _("Try again") . "</a>";
144 function check_faillog()
146 global $login_delay, $login_faillog, $login_max_attempts;
148 $user = $_SESSION["wa_current_user"]->user;
150 if (@$login_delay && (@$login_faillog[$user][$_SERVER['REMOTE_ADDR']] >= @$login_max_attempts) && (time() < $login_faillog[$user]['last'] + $login_delay))
156 Simple brute force attack detection is performed before connection to company database is open. Therefore access counters have to be stored in file.
157 Login attempts counter is created for every new user IP, which partialy prevent DOS attacks.
159 function write_login_filelog($login, $result)
161 global $login_faillog, $login_max_attempts, $path_to_root;
163 $user = $_SESSION["wa_current_user"]->user;
165 $ip = $_SERVER['REMOTE_ADDR'];
167 if (!isset($login_faillog[$user][$ip]) || $result) // init or reset on successfull login
168 $login_faillog[$user] = array($ip => 0, 'last' => '');
172 if ($login_faillog[$user][$ip] < @$login_max_attempts) {
174 $login_faillog[$user][$ip]++;
176 $login_faillog[$user][$ip] = 0; // comment out to restart counter only after successfull login.
177 error_log(sprintf(_("Brute force attack on account '%s' detected. Access for non-logged users temporarily blocked." ), $login));
179 $login_faillog[$user]['last'] = time();
184 $msg .= "Login attempts info.\n";
186 $msg .= "\$login_faillog = " .var_export($login_faillog, true). ";\n";
188 $filename = $path_to_root."/faillog.php";
190 if ((!file_exists($filename) && is_writable($path_to_root)) || is_writable($filename))
192 file_put_contents($filename, $msg);
196 //----------------------------------------------------------------------------------------
198 function check_page_security($page_security)
204 if (!$_SESSION["wa_current_user"]->check_user_access())
206 // notification after upgrade from pre-2.2 version
207 $msg = $_SESSION["wa_current_user"]->old_db ?
208 _("Security settings have not been defined for your user account.")
209 . "<br>" . _("Please contact your system administrator.")
210 : _("Please remove \$security_groups and \$security_headings arrays from config.php file!");
211 } elseif (!$_SESSION['SysPrefs']->db_ok && !$_SESSION["wa_current_user"]->can_access('SA_SOFTWAREUPGRADE')) {
212 $msg = _('Access to application has been blocked until database upgrade is completed by system administrator.');
217 end_page(@$_REQUEST['popup']);
222 if (!$_SESSION["wa_current_user"]->can_access_page($page_security))
225 echo "<center><br><br><br><b>";
226 echo _("The security settings on your account do not permit you to access this function");
228 echo "<br><br><br><br></center>";
229 end_page(@$_REQUEST['popup']);
232 if (!$_SESSION['SysPrefs']->db_ok
233 && !in_array($page_security, array('SA_SOFTWAREUPGRADE', 'SA_OPEN', 'SA_BACKUP')))
235 display_error(_('System is blocked after source upgrade until database is updated on System/Software Upgrade page'));
242 Helper function for setting page security level depeding on
243 GET start variable and/or some value stored in session variable.
244 Before the call $page_security should be set to default page_security value.
246 function set_page_security($value=null, $trans = array(), $gtrans = array())
248 global $page_security;
250 // first check is this is not start page call
251 foreach($gtrans as $key => $area)
252 if (isset($_GET[$key])) {
253 $page_security = $area;
257 // then check session value
258 if (isset($trans[$value])) {
259 $page_security = $trans[$value];
264 //-----------------------------------------------------------------------------
265 // Removing magic quotes from nested arrays/variables
267 function strip_quotes($data)
269 if(get_magic_quotes_gpc()) {
270 if(is_array($data)) {
271 foreach($data as $k => $v) {
272 $data[$k] = strip_quotes($data[$k]);
275 return stripslashes($data);
280 function html_cleanup(&$parms)
282 foreach($parms as $name => $value) {
283 // $value = @html_entity_decode($value, ENT_QUOTES, $_SESSION['language']->encoding);
284 if (is_array($value))
285 html_cleanup($parms[$name]);
287 $parms[$name] = @htmlspecialchars($value, ENT_QUOTES, $_SESSION['language']->encoding);
289 reset($parms); // needed for direct key() usage later throughout the sources
292 //============================================================================
295 function login_timeout()
297 // skip timeout on logout page
298 if ($_SESSION["wa_current_user"]->logged) {
299 $tout = $_SESSION["wa_current_user"]->timeout;
300 if ($tout && (time() > $_SESSION["wa_current_user"]->last_act + $tout))
302 $_SESSION["wa_current_user"]->logged = false;
304 $_SESSION["wa_current_user"]->last_act = time();
307 //============================================================================
308 if (!isset($path_to_root))
313 //----------------------------------------------------------------------------------------
314 // set to reasonable values if not set in config file (pre-2.3.12 installations)
316 if ((!isset($login_delay)) || ($login_delay < 0))
319 if ((!isset($login_max_attempts)) || ($login_max_attempts < 0))
320 $login_max_attempts = 3;
323 // Prevent register_globals vulnerability
324 if (isset($_GET['path_to_root']) || isset($_POST['path_to_root']))
325 die("Restricted access");
327 include_once($path_to_root . "/includes/errors.inc");
328 // colect all error msgs
329 set_error_handler('error_handler' /*, errtypes */);
331 include_once($path_to_root . "/includes/current_user.inc");
332 include_once($path_to_root . "/frontaccounting.php");
333 include_once($path_to_root . "/admin/db/security_db.inc");
334 include_once($path_to_root . "/includes/lang/language.php");
335 include_once($path_to_root . "/config_db.php");
336 include_once($path_to_root . "/includes/ajax.inc");
337 include_once($path_to_root . "/includes/ui/ui_msgs.inc");
338 include_once($path_to_root . "/includes/prefs/sysprefs.inc");
340 include_once($path_to_root . "/includes/hooks.inc");
342 // include all extensions hook files.
344 foreach ($installed_extensions as $ext)
346 if (file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
347 include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
351 Uncomment the setting below when using FA on shared hosting
352 to avoid unexpeced session timeouts.
353 Make sure this directory exists and is writable!
355 // ini_set('session.save_path', dirname(__FILE__).'/../tmp/');
357 ini_set('session.gc_maxlifetime', 36000); // 10hrs
358 ini_set('session.cache_limiter', 'private'); // prevent 'page expired' errors
360 $Session_manager = new SessionManager();
361 $Session_manager->sessionStart('FA'.md5(dirname(__FILE__)));
363 // this is to fix the "back-do-you-want-to-refresh" issue - thanx PHPFreaks
364 header("Cache-control: private");
366 include_once($path_to_root . "/config.php");
369 if ($login_delay > 0)
370 @include_once($path_to_root . "/faillog.php");
372 // Page Initialisation
373 if (!isset($_SESSION['language']) || !method_exists($_SESSION['language'], 'set_language'))
375 $l = array_search_value($dflt_lang, $installed_languages, 'code');
376 $_SESSION['language'] = new language($l['name'], $l['code'], $l['encoding'],
377 (isset($l['rtl']) && $l['rtl'] === true) ? 'rtl' : 'ltr');
380 $_SESSION['language']->set_language($_SESSION['language']->code);
383 include_once($path_to_root . "/includes/access_levels.inc");
384 include_once($path_to_root . "/version.php");
385 include_once($path_to_root . "/includes/main.inc");
387 // Ajax communication object
390 // js/php validation rules container
392 // bindings for editors
394 // page help. Currently help for function keys.
397 $Refs = new references();
399 // intercept all output to destroy it in case of ajax call
400 register_shutdown_function('end_flush');
401 ob_start('output_html',0);
403 if (!isset($_SESSION["wa_current_user"]))
404 $_SESSION["wa_current_user"] = new current_user();
407 html_cleanup($_POST);
408 html_cleanup($_REQUEST);
409 html_cleanup($_SERVER);
411 // logout.php is the only page we should have always
412 // accessable regardless of access level and current login status.
413 if (strstr($_SERVER['PHP_SELF'], 'logout.php') == false){
417 if (!$_SESSION["wa_current_user"]->old_db)
418 include_once($path_to_root . '/company/'.user_company().'/installed_extensions.php');
422 if (!$_SESSION["wa_current_user"]->logged_in())
425 if (!isset($_POST["user_name_entry_field"]) or $_POST["user_name_entry_field"] == "")
427 // strip ajax marker from uri, to force synchronous page reload
428 $_SESSION['timeout'] = array( 'uri'=>preg_replace('/JsHttpRequest=(?:(\d+)-)?([^&]+)/s',
429 '', @htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, $_SESSION['language']->encoding)),
432 include($path_to_root . "/access/login.php");
434 $Ajax->activate('_page_body');
437 if (isset($_POST["company_login_nickname"]) && !isset($_POST["company_login_name"])) {
438 for ($i = 0; $i < count($db_connections); $i++) {
439 if ($db_connections[$i]["name"] == $_POST["company_login_nickname"]) {
440 $_POST["company_login_name"] = $i;
441 unset($_POST["company_login_nickname"]);
442 break 1; // cannot pass variables to break from PHP v5.4 onwards
446 $succeed = isset($db_connections[$_POST["company_login_name"]]) &&
447 $_SESSION["wa_current_user"]->login($_POST["company_login_name"],
448 $_POST["user_name_entry_field"], $_POST["password"]);
449 // select full vs fallback ui mode on login
450 $_SESSION["wa_current_user"]->ui_mode = $_POST['ui_mode'];
453 // Incorrect password
456 $lang = &$_SESSION['language'];
457 $lang->set_language($_SESSION['language']->code);
460 set_global_connection();
462 if (!isset($_SESSION["App"])) {
463 $_SESSION["App"] = new front_accounting();
464 $_SESSION["App"]->init();
468 $SysPrefs = &$_SESSION['SysPrefs'];
470 // POST vars cleanup needed for direct reuse.
471 // We quote all values later with db_escape() before db update.
472 $_POST = strip_quotes($_POST);