prepare_cookies(); $this->init_session_storage(); if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) { sid_start_ob(); } if (NO_MOODLE_COOKIES) { $_SESSION = array(); $_SESSION['SESSION'] = new object(); $_SESSION['USER'] = new object(); } else { session_name('MoodleSession'.$CFG->sessioncookie); session_set_cookie_params(0, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure, $CFG->cookiehttponly); @session_start(); if (!isset($_SESSION['SESSION'])) { $_SESSION['SESSION'] = new object(); } if (!isset($_SESSION['USER'])) { $_SESSION['USER'] = new object(); } } $this->check_user_initialised(); $this->check_security(); } /** * Initialise $USER object, handles google access. * * @return void */ protected function check_user_initialised() { if (isset($_SESSION['USER']->id)) { // already set up $USER return; } $user = null; if (!empty($CFG->opentogoogle) and !NO_MOODLE_COOKIES) { if (!empty($_SERVER['HTTP_USER_AGENT'])) { // allow web spiders in as guest users if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) { $user = guest_user(); } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google $user = guest_user(); } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) { // Yahoo $user = guest_user(); } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) { // Zoomspider $user = guest_user(); } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) { // MSN Search $user = guest_user(); } } if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) { // automaticaly log in users coming from search engine results if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) { $user = guest_user(); } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) { $user = guest_user(); } } } if (!$user) { $user = new object(); $user->id = 0; // to enable proper function of $CFG->notloggedinroleid hack if (isset($CFG->mnet_localhost_id)) { $user->mnethostid = $CFG->mnet_localhost_id; } } session_set_user($user); } /** * Does various session security checks * @global void */ protected function check_security() { global $CFG; if (!empty($_SESSION['USER']->id) and !empty($CFG->tracksessionip)) { /// Make sure current IP matches the one for this session $remoteaddr = getremoteaddr(); if (empty($_SESSION['USER']->sessionip)) { $_SESSION['USER']->sessionip = $remoteaddr; } if ($_SESSION['USER']->sessionip != $remoteaddr) { // this is a security feature - terminate the session in case of any doubt $this->terminate(); print_error('sessionipnomatch2', 'error'); } } } /** * Terminates active moodle session */ public function terminate() { global $CFG, $SESSION, $USER; $_SESSION = array(); $SESSION = new object(); $USER = new object(); $USER->id = 0; if (isset($CFG->mnet_localhost_id)) { $USER->mnethostid = $CFG->mnet_localhost_id; } // Initialize variable to pass-by-reference to headers_sent(&$file, &$line) $file = null; $line = null; if (headers_sent($file, $line)) { error_log('Can not terminate session properly - headers were already sent in file: '.$file.' on line '.$line); } // now let's try to get a new session id and destroy the old one @session_regenerate_id(true); // close the session @session_write_close(); } /** * Prepare cookies and varions system settings */ protected function prepare_cookies() { global $CFG, $nomoodlecookie; if (!defined('NO_MOODLE_COOKIES')) { if (CLI_SCRIPT) { // CLI scripts can not have session define('NO_MOODLE_COOKIES', true); } else if (isset($nomoodlecookie)) { // backwards compatibility only define('NO_MOODLE_COOKIES', $nomoodlecookie); } else { define('NO_MOODLE_COOKIES', false); } } unset($nomoodlecookie); // cleanup if (!isset($CFG->cookiesecure) or (strpos($CFG->wwwroot, 'https://') !== 0 and empty($CFG->sslproxy))) { $CFG->cookiesecure = 0; } if (!isset($CFG->cookiehttponly)) { $CFG->cookiehttponly = 0; } /// Set sessioncookie and sessioncookiepath variable if it isn't already if (!isset($CFG->sessioncookie)) { $CFG->sessioncookie = ''; } if (!isset($CFG->sessioncookiedomain)) { $CFG->sessioncookiedomain = ''; } if (!isset($CFG->sessioncookiepath)) { $CFG->sessioncookiepath = '/'; } //discard session ID from POST, GET and globals to tighten security, //this session fixation prevention can not be used in cookieless mode if (empty($CFG->usesid)) { unset(${'MoodleSession'.$CFG->sessioncookie}); unset($_GET['MoodleSession'.$CFG->sessioncookie]); unset($_POST['MoodleSession'.$CFG->sessioncookie]); unset($_REQUEST['MoodleSession'.$CFG->sessioncookie]); } //compatibility hack for Moodle Cron, cookies not deleted, but set to "deleted" - should not be needed with NO_MOODLE_COOKIES in cron.php now if (!empty($_COOKIE['MoodleSession'.$CFG->sessioncookie]) && $_COOKIE['MoodleSession'.$CFG->sessioncookie] == "deleted") { unset($_COOKIE['MoodleSession'.$CFG->sessioncookie]); } } /** * Inits session storage. */ protected function init_session_storage() { global $CFG; /// Set up session handling if(empty($CFG->respectsessionsettings)) { if (true) { /// File-based sessions // Some distros disable GC by setting probability to 0 // overriding the PHP default of 1 // (gc_probability is divided by gc_divisor, which defaults to 1000) if (ini_get('session.gc_probability') == 0) { ini_set('session.gc_probability', 1); } if (!empty($CFG->sessiontimeout)) { ini_set('session.gc_maxlifetime', $CFG->sessiontimeout); } if (!file_exists($CFG->dataroot .'/sessions')) { make_upload_directory('sessions'); } ini_set('session.save_path', $CFG->dataroot .'/sessions'); } else { /// Database sessions // TODO: implement proper database session storage } } } } /** * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey * if one does not already exist, but does not overwrite existing sesskeys. Returns the * sesskey string if $USER exists, or boolean false if not. * * @uses $USER * @return string */ function sesskey() { global $USER; if (empty($USER->sesskey)) { $USER->sesskey = random_string(10); } return $USER->sesskey; } /** * For security purposes, this function will check that the currently * given sesskey (passed as a parameter to the script or this function) * matches that of the current user. * * @param string $sesskey optionally provided sesskey * @return bool */ function confirm_sesskey($sesskey=NULL) { global $USER; if (!empty($USER->ignoresesskey)) { return true; } if (empty($sesskey)) { $sesskey = required_param('sesskey', PARAM_RAW); // Check script parameters } return (sesskey() === $sesskey); } /** * Sets a moodle cookie with a weakly encrypted string * * @uses $CFG * @uses DAYSECS * @uses HOURSECS * @param string $thing The string to encrypt and place in a cookie */ function set_moodle_cookie($thing) { global $CFG; if ($thing == 'guest') { // Ignore guest account return; } $cookiename = 'MOODLEID_'.$CFG->sessioncookie; $days = 60; $seconds = DAYSECS*$days; // no need to set secure or http cookie only here - it is not secret setcookie($cookiename, '', time() - HOURSECS, $CFG->sessioncookiepath, $CFG->sessioncookiedomain); setcookie($cookiename, rc4encrypt($thing), time()+$seconds, $CFG->sessioncookiepath, $CFG->sessioncookiedomain); } /** * Gets a moodle cookie with a weakly encrypted string * * @uses $CFG * @return string */ function get_moodle_cookie() { global $CFG; $cookiename = 'MOODLEID_'.$CFG->sessioncookie; if (empty($_COOKIE[$cookiename])) { return ''; } else { $thing = rc4decrypt($_COOKIE[$cookiename]); return ($thing == 'guest') ? '': $thing; // Ignore guest account } } /** * Setup $USER object - called during login, loginas, etc. * Preloads capabilities and checks enrolment plugins * * @param object $user full user record object * @return void */ function session_set_user($user) { $_SESSION['USER'] = $user; check_enrolment_plugins($_SESSION['USER']); load_all_capabilities(); sesskey(); // init session key } /** * Is current $USER logged-in-as somebody else? * @return bool */ function session_is_loggedinas() { return !empty($_SESSION['USER']->realuser); } /** * Returns the $USER object ignoring current login-as session * @return object user object */ function session_get_realuser() { if (session_is_loggedinas()) { return $_SESSION['REALUSER']; } else { return $_SESSION['USER']; } } /** * Login as another user - no security checks here. * @param int $userid * @param object $context * @return void */ function session_loginas($userid, $context) { if (session_is_loggedinas()) { return; } // switch to fresh new $SESSION $_SESSION['REALSESSION'] = $_SESSION['SESSION']; $_SESSION['SESSION'] = new object(); /// Create the new $USER object with all details and reload needed capabilitites $_SESSION['REALUSER'] = $_SESSION['USER']; $user = get_complete_user_data('id', $userid); $user->realuser = $_SESSION['REALUSER']->id; $user->loginascontext = $context; session_set_user($user); } /** * Terminate login-as session * @return void */ function session_unloginas() { if (!session_is_loggedinas()) { return; } $_SESSION['SESSION'] = $_SESSION['REALSESSION']; unset($_SESSION['REALSESSION']); $_SESSION['USER'] = $_SESSION['REALUSER']; unset($_SESSION['REALUSER']); } /** * Sets up current user and course enviroment (lang, etc.) in cron. * Do not use outside of cron script! * * @param object $user full user object, null means default cron user (admin) * @param $course full course record, null means $SITE * @return void */ function cron_setup_user($user=null, $course=null) { global $CFG, $SITE; static $cronuser = null; static $cronsession = null; if (empty($cronuser)) { /// ignore admins timezone, language and locale - use site deafult instead! $cronuser = get_admin(); $cronuser->timezone = $CFG->timezone; $cronuser->lang = ''; $cronuser->theme = ''; $cronsession = array(); } if (!$user) { // cached default cron user (==modified admin for now) session_set_user($cronuser); $_SESSION['SESSION'] = $cronsession; } else { // emulate real user session - needed for caps in cron if ($_SESSION['USER']->id != $user->id) { session_set_user($user); $_SESSION['SESSION'] = array(); } } if ($course) { course_setup($course); } else { course_setup($SITE); } // TODO: it should be possible to improve perf by caching some limited number of users here ;-) } /** * Enable cookieless sessions by including $CFG->usesid=true; * in config.php. * Based on code from php manual by Richard at postamble.co.uk * Attempts to use cookies if cookies not present then uses session ids attached to all urls and forms to pass session id from page to page. * If site is open to google, google is given guest access as usual and there are no sessions. No session ids will be attached to urls for googlebot. * This doesn't require trans_sid to be turned on but this is recommended for better performance * you should put : * session.use_trans_sid = 1 * in your php.ini file and make sure that you don't have a line like this in your php.ini * session.use_only_cookies = 1 * @author Richard at postamble.co.uk and Jamie Pratt * @license http://www.gnu.org/copyleft/gpl.html GNU Public License */ /** * You won't call this function directly. This function is used to process * text buffered by php in an output buffer. All output is run through this function * before it is ouput. * @param string $buffer is the output sent from php * @return string the output sent to the browser */ function sid_ob_rewrite($buffer){ $replacements = array( '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*")([^"]*)(")/i', '/(<\s*(a|link|script|frame|area)\s[^>]*(href|src)\s*=\s*\')([^\']*)(\')/i'); $buffer = preg_replace_callback($replacements, 'sid_rewrite_link_tag', $buffer); $buffer = preg_replace('/