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();
80 // Grab current session ID and close both sessions to allow other scripts to use them
81 $newSession = session_id();
82 session_write_close();
83 // Set session ID to the new one, and start it back up again
85 session_id($newSession);
88 // Now we unset the obsolete and expiration values for the session we want to keep
89 unset($_SESSION['OBSOLETE']);
90 unset($_SESSION['EXPIRES']);
93 function validateSession()
95 if (isset($_SESSION['OBSOLETE']) && !isset($_SESSION['EXPIRES']) )
98 if (isset($_SESSION['EXPIRES']) && $_SESSION['EXPIRES'] < time())
105 function output_html($text)
107 global $before_box, $Ajax, $messages;
108 // Fatal errors are not send to error_handler,
109 // so we must check the output
110 if ($text && preg_match('/\bFatal error(<.*?>)?:(.*)/i', $text, $m)) {
111 $Ajax->aCommands = array(); // Don't update page via ajax on errors
112 $text = preg_replace('/\bFatal error(<.*?>)?:(.*)/i','', $text);
113 $messages[] = array(E_ERROR, $m[2], null, null);
116 return in_ajax() ? fmt_errors() : ($before_box.fmt_errors().$text);
118 //----------------------------------------------------------------------------------------
120 function kill_login()
125 //----------------------------------------------------------------------------------------
127 function login_fail()
129 global $path_to_root;
131 header("HTTP/1.1 401 Authorization Required");
132 echo "<center><br><br><font size='5' color='red'><b>" . _("Incorrect Password") . "<b></font><br><br>";
133 echo "<b>" . _("The user and password combination is not valid for the system.") . "<b><br><br>";
135 echo _("If you are not an authorized user, please contact your system administrator to obtain an account to enable you to use the system.");
136 echo "<br><a href='$path_to_root/index.php'>" . _("Try again") . "</a>";
143 function check_faillog()
145 global $login_delay, $login_faillog, $login_max_attempts;
147 $user = $_SESSION["wa_current_user"]->user;
149 if (@$login_delay && (@$login_faillog[$user][$_SERVER['REMOTE_ADDR']] >= @$login_max_attempts) && (time() < $login_faillog[$user]['last'] + $login_delay))
155 Simple brute force attack detection is performed before connection to company database is open. Therefore access counters have to be stored in file.
156 Login attempts counter is created for every new user IP, which partialy prevent DOS attacks.
158 function write_login_filelog($login, $result)
160 global $login_faillog, $login_max_attempts, $path_to_root;
162 $user = $_SESSION["wa_current_user"]->user;
164 $ip = $_SERVER['REMOTE_ADDR'];
166 if (!isset($login_faillog[$user][$ip]) || $result) // init or reset on successfull login
167 $login_faillog[$user] = array($ip => 0, 'last' => '');
171 if ($login_faillog[$user][$ip] < @$login_max_attempts) {
173 $login_faillog[$user][$ip]++;
175 $login_faillog[$user][$ip] = 0; // comment out to restart counter only after successfull login.
176 error_log(sprintf(_("Brute force attack on account '%s' detected. Access for non-logged users temporarily blocked." ), $login));
178 $login_faillog[$user]['last'] = time();
183 $msg .= "Login attempts info.\n";
185 $msg .= "\$login_faillog = " .var_export($login_faillog, true). ";\n";
187 $filename = $path_to_root."/faillog.php";
189 if ((!file_exists($filename) && is_writable($path_to_root)) || is_writable($filename))
191 file_put_contents($filename, $msg);
195 //----------------------------------------------------------------------------------------
197 function check_page_security($page_security)
203 if (!$_SESSION["wa_current_user"]->check_user_access())
205 // notification after upgrade from pre-2.2 version
206 $msg = $_SESSION["wa_current_user"]->old_db ?
207 _("Security settings have not been defined for your user account.")
208 . "<br>" . _("Please contact your system administrator.")
209 : _("Please remove \$security_groups and \$security_headings arrays from config.php file!");
210 } elseif (!$_SESSION['SysPrefs']->db_ok && !$_SESSION["wa_current_user"]->can_access('SA_SOFTWAREUPGRADE')) {
211 $msg = _('Access to application has been blocked until database upgrade is completed by system administrator.');
216 end_page(@$_REQUEST['popup']);
221 if (!$_SESSION["wa_current_user"]->can_access_page($page_security))
224 echo "<center><br><br><br><b>";
225 echo _("The security settings on your account do not permit you to access this function");
227 echo "<br><br><br><br></center>";
228 end_page(@$_REQUEST['popup']);
231 if (!$_SESSION['SysPrefs']->db_ok
232 && !in_array($page_security, array('SA_SOFTWAREUPGRADE', 'SA_OPEN', 'SA_BACKUP')))
234 display_error(_('System is blocked after source upgrade until database is updated on System/Software Upgrade page'));
241 Helper function for setting page security level depeding on
242 GET start variable and/or some value stored in session variable.
243 Before the call $page_security should be set to default page_security value.
245 function set_page_security($value=null, $trans = array(), $gtrans = array())
247 global $page_security;
249 // first check is this is not start page call
250 foreach($gtrans as $key => $area)
251 if (isset($_GET[$key])) {
252 $page_security = $area;
256 // then check session value
257 if (isset($trans[$value])) {
258 $page_security = $trans[$value];
263 //-----------------------------------------------------------------------------
264 // Removing magic quotes from nested arrays/variables
266 function strip_quotes($data)
268 if(get_magic_quotes_gpc()) {
269 if(is_array($data)) {
270 foreach($data as $k => $v) {
271 $data[$k] = strip_quotes($data[$k]);
274 return stripslashes($data);
279 function html_cleanup(&$parms)
281 foreach($parms as $name => $value) {
282 // $value = @html_entity_decode($value, ENT_QUOTES, $_SESSION['language']->encoding);
283 if (is_array($value))
284 html_cleanup($parms[$name]);
286 $parms[$name] = @htmlspecialchars($value, ENT_QUOTES, $_SESSION['language']->encoding);
288 reset($parms); // needed for direct key() usage later throughout the sources
291 //============================================================================
294 function login_timeout()
296 // skip timeout on logout page
297 if ($_SESSION["wa_current_user"]->logged) {
298 $tout = $_SESSION["wa_current_user"]->timeout;
299 if ($tout && (time() > $_SESSION["wa_current_user"]->last_act + $tout))
301 $_SESSION["wa_current_user"]->logged = false;
303 $_SESSION["wa_current_user"]->last_act = time();
306 //============================================================================
307 if (!isset($path_to_root))
312 //----------------------------------------------------------------------------------------
313 // set to reasonable values if not set in config file (pre-2.3.12 installations)
315 if ((!isset($login_delay)) || ($login_delay < 0))
318 if ((!isset($login_max_attempts)) || ($login_max_attempts < 0))
319 $login_max_attempts = 3;
322 // Prevent register_globals vulnerability
323 if (isset($_GET['path_to_root']) || isset($_POST['path_to_root']))
324 die("Restricted access");
326 include_once($path_to_root . "/includes/errors.inc");
327 // colect all error msgs
328 set_error_handler('error_handler' /*, errtypes */);
330 include_once($path_to_root . "/includes/current_user.inc");
331 include_once($path_to_root . "/frontaccounting.php");
332 include_once($path_to_root . "/admin/db/security_db.inc");
333 include_once($path_to_root . "/includes/lang/language.php");
334 include_once($path_to_root . "/config_db.php");
335 include_once($path_to_root . "/includes/ajax.inc");
336 include_once($path_to_root . "/includes/ui/ui_msgs.inc");
337 include_once($path_to_root . "/includes/prefs/sysprefs.inc");
339 include_once($path_to_root . "/includes/hooks.inc");
341 // include all extensions hook files.
343 foreach ($installed_extensions as $ext)
345 if (file_exists($path_to_root.'/'.$ext['path'].'/hooks.php'))
346 include_once($path_to_root.'/'.$ext['path'].'/hooks.php');
350 Uncomment the setting below when using FA on shared hosting
351 to avoid unexpeced session timeouts.
352 Make sure this directory exists and is writable!
354 // ini_set('session.save_path', dirname(__FILE__).'/../tmp/');
356 ini_set('session.gc_maxlifetime', 36000); // 10hrs
357 ini_set('session.cache_limiter', 'private'); // prevent 'page expired' errors
359 hook_session_start(@$_POST["company_login_name"]);
361 $Session_manager = new SessionManager();
362 $Session_manager->sessionStart('FA'.md5(dirname(__FILE__)));
364 // this is to fix the "back-do-you-want-to-refresh" issue - thanx PHPFreaks
365 header("Cache-control: private");
367 include_once($path_to_root . "/config.php");
370 if ($login_delay > 0)
371 @include_once($path_to_root . "/faillog.php");
373 // Page Initialisation
374 if (!isset($_SESSION['wa_current_user']) || !$_SESSION['wa_current_user']->logged_in()
375 || !isset($_SESSION['language']) || !method_exists($_SESSION['language'], 'set_language'))
377 $l = array_search_value($dflt_lang, $installed_languages, 'code');
378 $_SESSION['language'] = new language($l['name'], $l['code'], $l['encoding'],
379 (isset($l['rtl']) && $l['rtl'] === true) ? 'rtl' : 'ltr');
382 $_SESSION['language']->set_language($_SESSION['language']->code);
385 include_once($path_to_root . "/includes/access_levels.inc");
386 include_once($path_to_root . "/version.php");
387 include_once($path_to_root . "/includes/main.inc");
388 include_once($path_to_root . "/includes/app_entries.inc");
390 // Ajax communication object
393 // js/php validation rules container
395 // bindings for editors
397 // page help. Currently help for function keys.
400 $Refs = new references();
402 // intercept all output to destroy it in case of ajax call
403 register_shutdown_function('end_flush');
404 ob_start('output_html',0);
406 if (!isset($_SESSION["wa_current_user"]))
407 $_SESSION["wa_current_user"] = new current_user();
410 html_cleanup($_POST);
411 html_cleanup($_REQUEST);
412 html_cleanup($_SERVER);
414 // logout.php is the only page we should have always
415 // accessable regardless of access level and current login status.
416 if (strstr($_SERVER['PHP_SELF'], 'logout.php') == false){
420 if (!$_SESSION["wa_current_user"]->old_db)
421 include_once($path_to_root . '/company/'.user_company().'/installed_extensions.php');
425 if (!$_SESSION["wa_current_user"]->logged_in())
428 if (!isset($_POST["user_name_entry_field"]) or $_POST["user_name_entry_field"] == "")
430 // strip ajax marker from uri, to force synchronous page reload
431 $_SESSION['timeout'] = array( 'uri'=>preg_replace('/JsHttpRequest=(?:(\d+)-)?([^&]+)/s',
432 '', @htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, $_SESSION['language']->encoding)),
435 include($path_to_root . "/access/login.php");
437 $Ajax->activate('_page_body');
440 if (isset($_POST["company_login_nickname"]) && !isset($_POST["company_login_name"])) {
441 for ($i = 0; $i < count($db_connections); $i++) {
442 if ($db_connections[$i]["name"] == $_POST["company_login_nickname"]) {
443 $_POST["company_login_name"] = $i;
444 unset($_POST["company_login_nickname"]);
445 break 1; // cannot pass variables to break from PHP v5.4 onwards
449 $succeed = isset($db_connections[$_POST["company_login_name"]]) &&
450 $_SESSION["wa_current_user"]->login($_POST["company_login_name"],
451 $_POST["user_name_entry_field"], $_POST["password"]);
452 // select full vs fallback ui mode on login
453 $_SESSION["wa_current_user"]->ui_mode = $_POST['ui_mode'];
456 // Incorrect password
461 { set_global_connection();
463 db_set_encoding($_SESSION['language']->encoding);
466 if (!isset($_SESSION["App"])) {
467 $_SESSION["App"] = new front_accounting();
468 $_SESSION["App"]->init();
472 $SysPrefs = &$_SESSION['SysPrefs'];
474 // POST vars cleanup needed for direct reuse.
475 // We quote all values later with db_escape() before db update.
476 $_POST = strip_quotes($_POST);