TYPO3 API  SVNRelease
class.tx_install_session.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2009-2011 Ernesto Baschny <ernst@cron-it.de>
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 
00028 /**
00029  * Secure session handling for the install tool.
00030  *
00031  * @author  Ernesto Baschny <ernst@cron-it.de>
00032  *
00033  * @package TYPO3
00034  * @subpackage tx_install
00035  *
00036  * @version $Id: class.tx_install_session.php 10636 2011-02-25 16:30:29Z baschny $
00037  */
00038 class tx_install_session {
00039 
00040     /**
00041      * The path to our typo3temp (where we can write our sessions). Set in the
00042      * constructor.
00043      *
00044      * @var string
00045      */
00046     private $typo3tempPath;
00047 
00048     /**
00049      * Path where to store our session files in typo3temp. %s will be
00050      * non-guesseable.
00051      *
00052      * @var string
00053      */
00054     private $sessionPath = 'sessions%s';
00055 
00056     /**
00057      * the cookie to store the session ID of the install tool
00058      *
00059      * @var string
00060      */
00061     private $cookieName = 'Typo3InstallTool';
00062 
00063     /**
00064      * time (minutes) to expire an ununsed session
00065      *
00066      * @var integer
00067      */
00068     private $expireTimeInMinutes = 60;
00069 
00070     /**
00071      * time (minutes) to generate a new session id for our current session
00072      *
00073      * @var integer
00074      */
00075     private $regenerateSessionIdTime = 5;
00076 
00077     /**
00078      * part of the referer when the install tool has been called from the backend
00079      *
00080      * @var string
00081      */
00082     private $backendFile = 'backend.php';
00083 
00084     /**
00085      * Constructor. Starts PHP session handling in our own private store
00086      *
00087      * Side-effect: might set a cookie, so must be called before any other output.
00088      */
00089     public function __construct() {
00090         $this->typo3tempPath = PATH_site . 'typo3temp/';
00091 
00092         // Start our PHP session early so that hasSession() works
00093         $sessionSavePath = $this->getSessionSavePath();
00094         if (!is_dir($sessionSavePath)) {
00095             if (!t3lib_div::mkdir($sessionSavePath)) {
00096                 throw new Exception('<p><strong>Could not create session folder in typo3temp/.</strong></p><p>Make sure it is writeable!</p>');
00097             }
00098             t3lib_div::writeFile($sessionSavePath.'/.htaccess', 'Order deny, allow'."\n".'Deny from all'."\n");
00099             $indexContent = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">';
00100             $indexContent .= '<HTML><HEAD<TITLE></TITLE><META http-equiv=Refresh Content="0; Url=../../">';
00101             $indexContent .= '</HEAD></HTML>';
00102             t3lib_div::writeFile($sessionSavePath.'/index.html', $indexContent);
00103         }
00104         // Register our "save" session handler
00105         session_set_save_handler(
00106             array($this, 'open'),
00107             array($this, 'close'),
00108             array($this, 'read'),
00109             array($this, 'write'),
00110             array($this, 'destroy'),
00111             array($this, 'gc')
00112         );
00113         session_save_path($sessionSavePath);
00114         session_name($this->cookieName);
00115         ini_set('session.cookie_path', t3lib_div::getIndpEnv('TYPO3_SITE_PATH'));
00116         // Always call the garbage collector to clean up stale session files
00117         ini_set('session.gc_probability', 100);
00118         ini_set('session.gc_divisor', 100);
00119         ini_set('session.gc_maxlifetime', $this->expireTimeInMinutes*2*60);
00120         if (version_compare(phpversion(), '5.2', '<')) {
00121             ini_set('session.cookie_httponly', TRUE);
00122         }
00123         if (ini_get('session.auto_start')) {
00124             $sessionCreationError = '<p><strong>Error: session.auto-start is enabled</strong></p>';
00125             $sessionCreationError .= '<p>The PHP option session.auto-start is enabled. Disable this option in php.ini or .htaccess:</p>';
00126             $sessionCreationError .= '<pre>php_value session.auto_start Off</pre>';
00127             throw new Exception($sessionCreationError);
00128         } else if (defined('SID')) {
00129             $sessionCreationError = '<p><strong>Error: Session already started by session_start().</strong></p>';
00130             $sessionCreationError .= '<p>Make sure no installed extension is starting a session in its ext_localconf.php or ext_tables.php.</p>';
00131             throw new Exception($sessionCreationError);
00132         }
00133         session_start();
00134     }
00135 
00136     /**
00137      * Returns the path where to store our session files
00138      */
00139     private function getSessionSavePath() {
00140         return sprintf(
00141             $this->typo3tempPath . $this->sessionPath,
00142             md5(
00143                 'session:' .
00144                     $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword']
00145             )
00146         );
00147     }
00148 
00149     /**
00150      * Starts a new session
00151      *
00152      * @return string The session ID
00153      */
00154     public function startSession() {
00155         $_SESSION['created'] = time();
00156             // Be sure to use our own session id, so create a new one
00157         return $this->renewSession();
00158     }
00159 
00160     /**
00161      * Destroys a session
00162      *
00163      */
00164     public function destroySession() {
00165         session_destroy();
00166     }
00167 
00168     /**
00169      * Generates a new session ID and sends it to the client.
00170      *
00171      * Also moves session information from the old session to the new one
00172      * (in PHP 5.1 or later)
00173      *
00174      * @return string the new session ID
00175      */
00176     private function renewSession() {
00177         if (version_compare(phpversion(), '5.1', '<')) {
00178             session_regenerate_id(TRUE);
00179         } else {
00180             session_regenerate_id();
00181         }
00182         return session_id();
00183     }
00184 
00185     /**
00186      * Checks whether we already have an active session.
00187      *
00188      * @return boolean true if there is an active session, false otherwise
00189      */
00190     public function hasSession() {
00191         return (isset($_SESSION['created']));
00192     }
00193 
00194     /**
00195      * Returns the session ID of the running session.
00196      *
00197      * @return string the session ID
00198      */
00199     public function getSessionId() {
00200         return session_id();
00201     }
00202 
00203     /**
00204      * Returns a session hash, which can only be calculated by the server.
00205      * Used to store our session files without exposing the session ID.
00206      *
00207      * @param string An alternative session ID. Defaults to our current session ID
00208      *
00209      * @return string the session hash
00210      */
00211     private function getSessionHash($sessionId = '') {
00212         if (!$sessionId) {
00213             $sessionId = $this->getSessionId();
00214         }
00215         return md5($GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'].'|'.$sessionId);
00216     }
00217 
00218     /**
00219      * Marks this session as an "authorized" one (login successful).
00220      * Should only be called if:
00221      * a) we have a valid session running
00222      * b) the "password" or some other authorization mechanism really matched
00223      *
00224      * @return void
00225      */
00226     public function setAuthorized() {
00227         $_SESSION['authorized'] = TRUE;
00228         $_SESSION['lastSessionId'] = time();
00229         $_SESSION['tstamp'] = time();
00230         $_SESSION['expires'] = (time() + ($this->expireTimeInMinutes*60));
00231             // Renew the session id to avoid session fixation
00232         $this->renewSession();
00233     }
00234 
00235     /**
00236      * Check if we have an already authorized session
00237      *
00238      * @return boolean True if this session has been authorized before (by a correct password)
00239      */
00240     public function isAuthorized() {
00241         if (!$_SESSION['authorized']) {
00242             return FALSE;
00243         }
00244         if ($_SESSION['expires'] < time()) {
00245             // This session has already expired
00246             return FALSE;
00247         }
00248         return TRUE;
00249     }
00250 
00251     /**
00252      * Check if our session is expired.
00253      * Useful only right after a false "isAuthorized" to see if this is the
00254      * reason for not being authorized anymore.
00255      *
00256      * @return boolean True if an authorized session exists, but is expired
00257      */
00258     public function isExpired() {
00259         if (!$_SESSION['authorized']) {
00260             // Session never existed, means it is not "expired"
00261             return FALSE;
00262         }
00263         if ($_SESSION['expires'] < time()) {
00264             // This session was authorized before, but has expired
00265             return TRUE;
00266         }
00267         return FALSE;
00268     }
00269 
00270     /**
00271      * Refreshes our session information, rising the expire time.
00272      * Also generates a new session ID every 5 minutes to minimize the risk of
00273      * session hijacking.
00274      *
00275      * @return void
00276      */
00277     public function refreshSession() {
00278         $_SESSION['tstamp'] = time();
00279         $_SESSION['expires'] = time() + ($this->expireTimeInMinutes*60);
00280         if (time() > $_SESSION['lastSessionId']+$this->regenerateSessionIdTime*60) {
00281             // Renew our session ID
00282             $_SESSION['lastSessionId'] = time();
00283             $this->renewSession();
00284         }
00285     }
00286 
00287 
00288     /*************************
00289      *
00290      * PHP session handling with "secure" session files (hashed session id)
00291      * see http://www.php.net/manual/en/function.session-set-save-handler.php
00292      *
00293      *************************/
00294 
00295     /**
00296      * Returns the file where to store our session data
00297      *
00298      * @return string A filename
00299      */
00300     private function getSessionFile($id) {
00301         $sessionSavePath = $this->getSessionSavePath();
00302         return $sessionSavePath . '/hash_' . $this->getSessionHash($id);
00303     }
00304 
00305     /**
00306      * Open function. See @session_set_save_handler
00307      *
00308      * @param string $savePath
00309      * @param string $sessionName
00310      * @return boolean
00311      */
00312     public function open($savePath, $sessionName) {
00313         return TRUE;
00314     }
00315 
00316     /**
00317      * Close function. See @session_set_save_handler
00318      *
00319      * @return boolean
00320      */
00321     public function close() {
00322         return TRUE;
00323     }
00324 
00325     /**
00326      * Read session data. See @session_set_save_handler
00327      *
00328      * @param string The session id
00329      *
00330      * @return string
00331      */
00332     public function read($id) {
00333         $sessionFile = $this->getSessionFile($id);
00334         return (string) @file_get_contents($sessionFile);
00335     }
00336 
00337     /**
00338      * Write session data. See @session_set_save_handler
00339      *
00340      * @param string The session id
00341      * @param string The data to be stored
00342      *
00343      * @return boolean
00344      */
00345     public function write($id, $sessionData) {
00346         $sessionFile = $this->getSessionFile($id);
00347         return t3lib_div::writeFile($sessionFile, $sessionData);
00348     }
00349 
00350     /**
00351      * Destroys one session. See @session_set_save_handler
00352      *
00353      * @param string The session id
00354      *
00355      * @return string
00356      */
00357     public function destroy($id) {
00358         $sessionFile = $this->getSessionFile($id);
00359         return(@unlink($sessionFile));
00360     }
00361 
00362     /**
00363      * Garbage collect session info. See @session_set_save_handler
00364      *
00365      * @param integer The setting of session.gc_maxlifetime
00366      *
00367      * @return boolean
00368      */
00369     public function gc($maxLifeTime) {
00370         $sessionSavePath = $this->getSessionSavePath();
00371         $files = glob($sessionSavePath . '/hash_*');
00372         if (!is_array($files)) {
00373             return TRUE;
00374         }
00375         foreach ($files as $filename) {
00376             if (filemtime($filename) + ($this->expireTimeInMinutes*60) < time()) {
00377                 @unlink($filename);
00378             }
00379         }
00380         return TRUE;
00381     }
00382 
00383     /**
00384      * Writes the session data at the end, to overcome a PHP APC bug.
00385      *
00386      * Writes the session data in a proper context that is not affected by the APC bug:
00387      * http://pecl.php.net/bugs/bug.php?id=16721.
00388      *
00389      * This behaviour was introduced in #17511, where self::write() made use of t3lib_div
00390      * which due to the APC bug throws a "Fatal error: Class 't3lib_div' not found"
00391      * (and the session data is not saved). Calling session_write_close() at this point
00392      * seems to be the most easy solution, acording to PHP author.
00393      *
00394      * @return void
00395      */
00396     public function __destruct() {
00397         session_write_close();
00398     }
00399 }
00400 
00401 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_session.php'])) {
00402     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/install/mod/class.tx_install_session.php']);
00403 }
00404 
00405 ?>