Skip to content
Snippets Groups Projects
Commit 933c1969 authored by Jaime Pérez Crespo's avatar Jaime Pérez Crespo
Browse files

Fix memory leak in session serialization (issue #594)

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@3356 44740490-163a-0410-bde0-09ae8108e29a
parent f65a37c4
No related branches found
No related tags found
No related merge requests found
...@@ -46,7 +46,7 @@ class SimpleSAML_Memcache { ...@@ -46,7 +46,7 @@ class SimpleSAML_Memcache {
continue; continue;
} }
/* Deserialize the object. */ /* Unserialize the object. */
$info = unserialize($serializedInfo); $info = unserialize($serializedInfo);
/* /*
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* @package simpleSAMLphp * @package simpleSAMLphp
* @version $Id$ * @version $Id$
*/ */
class SimpleSAML_Session { class SimpleSAML_Session implements Serializable {
/** /**
* This is a timeout value for setData, which indicates that the data should be deleted * This is a timeout value for setData, which indicates that the data should be deleted
...@@ -95,7 +95,7 @@ class SimpleSAML_Session { ...@@ -95,7 +95,7 @@ class SimpleSAML_Session {
/** /**
* This is an array of objects which will autoexpire after a set time. It is used * This is an array of objects which will expire automatically after a set time. It is used
* where one needs to store some information - for example a logout request, but doesn't * where one needs to store some information - for example a logout request, but doesn't
* want it to be stored forever. * want it to be stored forever.
* *
...@@ -159,7 +159,7 @@ class SimpleSAML_Session { ...@@ -159,7 +159,7 @@ class SimpleSAML_Session {
/** /**
* private constructor restricts instantiaton to getInstance() * Private constructor that restricts instantiation to getInstance().
*/ */
private function __construct($transient = FALSE) { private function __construct($transient = FALSE) {
...@@ -188,6 +188,43 @@ class SimpleSAML_Session { ...@@ -188,6 +188,43 @@ class SimpleSAML_Session {
} }
} }
/**
* Serialize this session into a string that can be stored.
*
* @return string The serialized session.
*/
public function serialize() {
return serialize(get_object_vars(self::$instance));
}
/**
* Restore a previously serialized session into the current instance of the session. If no session exists
* already, a new one will be created.
*
* @param string $data A session previously serialized.
*/
public function unserialize($data) {
/* Check if we already have initialized the singleton. */
if (!isset(self::$instance)) {
/* Create a new one. */
self::$instance = new SimpleSAML_Session();
}
/* Populate it with the unserialized values. */
$data = unserialize($data);
if (is_array($data)) {
foreach ($data as $k => $v) {
$this->$k = $v;
}
}
/* TODO: Remove for version 1.8. */
if ($this->authData === NULL) {
$this->upgradeAuthData();
}
}
/** /**
* Upgrade this session object to use the $authData property. * Upgrade this session object to use the $authData property.
...@@ -244,22 +281,10 @@ class SimpleSAML_Session { ...@@ -244,22 +281,10 @@ class SimpleSAML_Session {
/** /**
* This function is called after this class has been deserialized. * Retrieves the current session. Will create a new one if there is none.
*/
public function __wakeup() {
$this->addShutdownFunction();
/* TODO: Remove for version 1.8. */
if ($this->authData === NULL) {
$this->upgradeAuthData();
}
}
/**
* Retrieves the current session. Will create a new session if there isn't a session.
* *
* @return The current session. * @throws Exception If unable to initialize the session.
* @return SimpleSAML_Session The current session.
*/ */
public static function getInstance() { public static function getInstance() {
...@@ -335,7 +360,7 @@ class SimpleSAML_Session { ...@@ -335,7 +360,7 @@ class SimpleSAML_Session {
/** /**
* Retrieve if session is transient. * Retrieve if session is transient.
* *
* @return boolean The session transient flag. * @return boolean The session transient flag.
*/ */
public function isTransient() { public function isTransient() {
return $this->transient; return $this->transient;
...@@ -345,6 +370,8 @@ class SimpleSAML_Session { ...@@ -345,6 +370,8 @@ class SimpleSAML_Session {
/** /**
* Get a unique ID that will be permanent for this session. * Get a unique ID that will be permanent for this session.
* Used for debugging and tracing log files related to a session. * Used for debugging and tracing log files related to a session.
*
* @return string The unique ID.
*/ */
public function getTrackID() { public function getTrackID() {
return $this->trackid; return $this->trackid;
...@@ -352,7 +379,9 @@ class SimpleSAML_Session { ...@@ -352,7 +379,9 @@ class SimpleSAML_Session {
/** /**
* Who authorized this session. could be in example saml2, shib13, login,login-admin etc. * Who authorized this session. Could be for example 'saml2', 'shib13', 'login', 'login-admin' etc.
*
* @return string Who authorized this session.
*/ */
public function getAuthority() { public function getAuthority() {
return $this->authority; return $this->authority;
...@@ -364,25 +393,28 @@ class SimpleSAML_Session { ...@@ -364,25 +393,28 @@ class SimpleSAML_Session {
* The complete request is not stored, instead the values that will be needed later * The complete request is not stored, instead the values that will be needed later
* are stored in an assoc array. * are stored in an assoc array.
* *
* @param $protocol saml2 or shib13 * @param string $protocol saml2 or shib13
* @param $requestid The request id used as a key to lookup the cache. * @param string $requestid The request id used as a key to lookup the cache.
* *
* @return Returns an assoc array of cached variables associated with the * @return array Returns an assoc array of cached variables associated with the
* authentication request. * authentication request.
*/ */
public function getAuthnRequest($protocol, $requestid) { public function getAuthnRequest($protocol, $requestid) {
SimpleSAML_Logger::debug('Library - Session: Get authnrequest from cache ' . $protocol . ' time:' . time() . ' id: '. $requestid ); SimpleSAML_Logger::debug('Library - Session: Get authnrequest from cache ' . $protocol . ' time:' . time() .
' id: '. $requestid );
$type = 'AuthnRequest-' . $protocol; $type = 'AuthnRequest-' . $protocol;
$authnRequest = $this->getData($type, $requestid); $authnRequest = $this->getData($type, $requestid);
if($authnRequest === NULL) { if($authnRequest === NULL) {
/* /*
* Could not find requested ID. Throw an error. Could be that it is never set, or that it is deleted due to age. * Could not find requested ID. Throw an error. Could be that it is never set, or that it is deleted
* due to age.
*/ */
throw new Exception('Could not find cached version of authentication request with ID ' . $requestid . ' (' . $protocol . ')'); throw new Exception('Could not find cached version of authentication request with ID ' . $requestid .
' (' . $protocol . ')');
} }
return $authnRequest; return $authnRequest;
...@@ -392,13 +424,14 @@ class SimpleSAML_Session { ...@@ -392,13 +424,14 @@ class SimpleSAML_Session {
/** /**
* This method sets a cached assoc array to the authentication request cache storage. * This method sets a cached assoc array to the authentication request cache storage.
* *
* @param $protocol saml2 or shib13 * @param string $protocol 'saml2' or 'shib13'
* @param $requestid The request id used as a key to lookup the cache. * @param string $requestid The request id used as a key to lookup the cache.
* @param $cache The assoc array that will be stored. * @param array $cache The assoc array that will be stored.
*/ */
public function setAuthnRequest($protocol, $requestid, array $cache) { public function setAuthnRequest($protocol, $requestid, array $cache) {
SimpleSAML_Logger::debug('Library - Session: Set authnrequest ' . $protocol . ' time:' . time() . ' size:' . count($cache) . ' id: '. $requestid ); SimpleSAML_Logger::debug('Library - Session: Set authnrequest ' . $protocol . ' time:' . time() . ' size:' .
count($cache) . ' id: '. $requestid );
$type = 'AuthnRequest-' . $protocol; $type = 'AuthnRequest-' . $protocol;
$this->setData($type, $requestid, $cache); $this->setData($type, $requestid, $cache);
...@@ -408,7 +441,7 @@ class SimpleSAML_Session { ...@@ -408,7 +441,7 @@ class SimpleSAML_Session {
/** /**
* Set the IdP we are authenticated against. * Set the IdP we are authenticated against.
* *
* @param string|NULL $idp Our current IdP, or NULL if we aren't authenticated with an IdP. * @param string|NULL $idp Our current IdP, or NULL if we aren't authenticated with an IdP.
*/ */
public function setIdP($idp) { public function setIdP($idp) {
assert('is_string($idp) || is_null($idp)'); assert('is_string($idp) || is_null($idp)');
...@@ -428,7 +461,7 @@ class SimpleSAML_Session { ...@@ -428,7 +461,7 @@ class SimpleSAML_Session {
/** /**
* Retrieve the IdP we are currently authenticated against. * Retrieve the IdP we are currently authenticated against.
* *
* @return string|NULL Our current IdP, or NULL if we aren't authenticated with an IdP. * @return string|NULL Our current IdP, or NULL if we aren't authenticated with an IdP.
*/ */
public function getIdP() { public function getIdP() {
if (!isset($this->authData[$this->authority]['saml:sp:IdP'])) { if (!isset($this->authData[$this->authority]['saml:sp:IdP'])) {
...@@ -441,7 +474,7 @@ class SimpleSAML_Session { ...@@ -441,7 +474,7 @@ class SimpleSAML_Session {
/** /**
* Set the SessionIndex we received from our IdP. * Set the SessionIndex we received from our IdP.
* *
* @param string|NULL $sessionindex Our SessionIndex. * @param string|NULL $sessionindex Our SessionIndex.
*/ */
public function setSessionIndex($sessionindex) { public function setSessionIndex($sessionindex) {
assert('is_string($sessionindex) || is_null($sessionindex)'); assert('is_string($sessionindex) || is_null($sessionindex)');
...@@ -460,7 +493,7 @@ class SimpleSAML_Session { ...@@ -460,7 +493,7 @@ class SimpleSAML_Session {
/** /**
* Retrieve our SessionIndex. * Retrieve our SessionIndex.
* *
* @return string|NULL Our SessionIndex. * @return string|NULL Our SessionIndex.
*/ */
public function getSessionIndex() { public function getSessionIndex() {
if (!isset($this->authData[$this->authority]['saml:sp:SessionIndex'])) { if (!isset($this->authData[$this->authority]['saml:sp:SessionIndex'])) {
...@@ -473,7 +506,7 @@ class SimpleSAML_Session { ...@@ -473,7 +506,7 @@ class SimpleSAML_Session {
/** /**
* Set our current NameID. * Set our current NameID.
* *
* @param array|NULL $nameid The NameID we received from the IdP * @param array|NULL $nameid The NameID we received from the IdP
*/ */
public function setNameID($nameid) { public function setNameID($nameid) {
assert('is_array($nameid) || is_null($nameid)'); assert('is_array($nameid) || is_null($nameid)');
...@@ -505,7 +538,7 @@ class SimpleSAML_Session { ...@@ -505,7 +538,7 @@ class SimpleSAML_Session {
/** /**
* Set remember me expire time. * Set remember me expire time.
* *
* @param int $expire Unix timestamp when remember me session cookies expire. * @param int $expire Unix timestamp when remember me session cookies expire.
*/ */
public function setRememberMeExpire($expire = NULL) { public function setRememberMeExpire($expire = NULL) {
assert('is_int($expire) || is_null($expire)'); assert('is_int($expire) || is_null($expire)');
...@@ -543,7 +576,8 @@ class SimpleSAML_Session { ...@@ -543,7 +576,8 @@ class SimpleSAML_Session {
if ($this->authToken !== NULL) { if ($this->authToken !== NULL) {
$globalConfig = SimpleSAML_Configuration::getInstance(); $globalConfig = SimpleSAML_Configuration::getInstance();
$sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'), $this->authToken, $params); $sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename',
'SimpleSAMLAuthToken'), $this->authToken, $params);
} }
} }
...@@ -553,8 +587,8 @@ class SimpleSAML_Session { ...@@ -553,8 +587,8 @@ class SimpleSAML_Session {
* *
* If the user already has logged in, the user will be logged out first. * If the user already has logged in, the user will be logged out first.
* *
* @param string $authority The authority the user logged in with. * @param string $authority The authority the user logged in with.
* @param array|NULL $data The authentication data for this authority. * @param array|NULL $data The authentication data for this authority.
*/ */
public function doLogin($authority, array $data = NULL) { public function doLogin($authority, array $data = NULL) {
assert('is_string($authority)'); assert('is_string($authority)');
...@@ -593,10 +627,13 @@ class SimpleSAML_Session { ...@@ -593,10 +627,13 @@ class SimpleSAML_Session {
$this->authToken = SimpleSAML_Utilities::generateID(); $this->authToken = SimpleSAML_Utilities::generateID();
$sessionHandler = SimpleSAML_SessionHandler::getSessionHandler(); $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
if (!$this->transient && (!empty($data['RememberMe']) || $this->rememberMeExpire) && $globalConfig->getBoolean('session.rememberme.enable', FALSE)) { if (!$this->transient && (!empty($data['RememberMe']) || $this->rememberMeExpire) &&
$this->setRememberMeExpire(); $globalConfig->getBoolean('session.rememberme.enable', FALSE)) {
$this->setRememberMeExpire();
} else { } else {
$sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'), $this->authToken); $sessionHandler->setCookie($globalConfig->getString('session.authtoken.cookiename',
'SimpleSAMLAuthToken'), $this->authToken);
} }
} }
...@@ -606,7 +643,7 @@ class SimpleSAML_Session { ...@@ -606,7 +643,7 @@ class SimpleSAML_Session {
* *
* This function will call any registered logout handlers before marking the user as logged out. * This function will call any registered logout handlers before marking the user as logged out.
* *
* @param string|NULL $authority The authentication source we are logging out of. * @param string|NULL $authority The authentication source we are logging out of.
*/ */
public function doLogout($authority = NULL) { public function doLogout($authority = NULL) {
...@@ -646,8 +683,8 @@ class SimpleSAML_Session { ...@@ -646,8 +683,8 @@ class SimpleSAML_Session {
/** /**
* Set the lifetime for authentication source. * Set the lifetime for authentication source.
* *
* @param string $authority The authentication source we are setting expire time for. * @param string $authority The authentication source we are setting expire time for.
* @param int $expire The number of seconds authentication source is valid. * @param int $expire The number of seconds authentication source is valid.
*/ */
public function setAuthorityExpire($authority, $expire = NULL) { public function setAuthorityExpire($authority, $expire = NULL) {
assert('isset($this->authData[$authority])'); assert('isset($this->authData[$authority])');
...@@ -667,7 +704,7 @@ class SimpleSAML_Session { ...@@ -667,7 +704,7 @@ class SimpleSAML_Session {
/** /**
* Set the lifetime of our current authentication session. * Set the lifetime of our current authentication session.
* *
* @param int $duration The number of seconds this authentication session is valid. * @param int $duration The number of seconds this authentication session is valid.
*/ */
public function setSessionDuration($duration) { public function setSessionDuration($duration) {
assert('is_int($duration)'); assert('is_int($duration)');
...@@ -692,7 +729,8 @@ class SimpleSAML_Session { ...@@ -692,7 +729,8 @@ class SimpleSAML_Session {
assert('is_string($authority)'); assert('is_string($authority)');
if (!isset($this->authData[$authority])) { if (!isset($this->authData[$authority])) {
SimpleSAML_Logger::debug('Session: '. var_export($authority, TRUE) .' not valid because we are not authenticated.'); SimpleSAML_Logger::debug('Session: '. var_export($authority, TRUE) .
' not valid because we are not authenticated.');
return FALSE; return FALSE;
} }
...@@ -710,7 +748,7 @@ class SimpleSAML_Session { ...@@ -710,7 +748,7 @@ class SimpleSAML_Session {
/** /**
* If the user is authenticated, how much time is left of the session. * If the user is authenticated, how much time is left of the session.
* *
* @return int The number of seconds until the session expires. * @return int The number of seconds until the session expires.
*/ */
public function remainingTime() { public function remainingTime() {
...@@ -726,7 +764,7 @@ class SimpleSAML_Session { ...@@ -726,7 +764,7 @@ class SimpleSAML_Session {
/** /**
* Is the user authenticated. This function does not check the session duration. * Is the user authenticated. This function does not check the session duration.
* *
* @return bool TRUE if the user is authenticated, FALSE otherwise. * @return bool TRUE if the user is authenticated, FALSE otherwise.
*/ */
public function isAuthenticated() { public function isAuthenticated() {
return isset($this->authData[$this->authority]); return isset($this->authData[$this->authority]);
...@@ -736,7 +774,7 @@ class SimpleSAML_Session { ...@@ -736,7 +774,7 @@ class SimpleSAML_Session {
/** /**
* Retrieve the time the user was authenticated. * Retrieve the time the user was authenticated.
* *
* @return int|NULL The timestamp for when the user was authenticated. NULL if the user hasn't authenticated. * @return int|NULL The timestamp for when the user was authenticated. NULL if the user hasn't authenticated.
*/ */
public function getAuthnInstant() { public function getAuthnInstant() {
...@@ -793,8 +831,8 @@ class SimpleSAML_Session { ...@@ -793,8 +831,8 @@ class SimpleSAML_Session {
/** /**
* Set the values of a single attribute. * Set the values of a single attribute.
* *
* @param string $name The name of the attribute. * @param string $name The name of the attribute.
* @param array $value The values of the attribute. * @param array $value The values of the attribute.
*/ */
public function setAttribute($name, $value) { public function setAttribute($name, $value) {
assert('isset($this->authData[$this->authority])'); assert('isset($this->authData[$this->authority])');
...@@ -807,7 +845,7 @@ class SimpleSAML_Session { ...@@ -807,7 +845,7 @@ class SimpleSAML_Session {
/** /**
* Calculates the size of the session object after serialization * Calculates the size of the session object after serialization
* *
* @return The size of the session measured in bytes. * @return int The size of the session measured in bytes.
*/ */
public function getSize() { public function getSize() {
$s = serialize($this); $s = serialize($this);
...@@ -818,8 +856,9 @@ class SimpleSAML_Session { ...@@ -818,8 +856,9 @@ class SimpleSAML_Session {
/** /**
* This function registers a logout handler. * This function registers a logout handler.
* *
* @param $classname The class which contains the logout handler. * @param string $classname The class which contains the logout handler.
* @param $functionname The logout handler function. * @param string $functionname The logout handler function.
* @throws Exception If the handler is not a valid function or method.
*/ */
public function registerLogoutHandler($classname, $functionname) { public function registerLogoutHandler($classname, $functionname) {
assert('isset($this->authData[$this->authority])'); assert('isset($this->authData[$this->authority])');
...@@ -840,7 +879,8 @@ class SimpleSAML_Session { ...@@ -840,7 +879,8 @@ class SimpleSAML_Session {
/** /**
* This function calls all registered logout handlers. * This function calls all registered logout handlers.
* *
* @param string $authority The authentication source we are logging out from. * @param string $authority The authentication source we are logging out from.
* @throws Exception If the handler is not a valid function or method.
*/ */
private function callLogoutHandlers($authority) { private function callLogoutHandlers($authority) {
assert('is_string($authority)'); assert('is_string($authority)');
...@@ -957,17 +997,20 @@ class SimpleSAML_Session { ...@@ -957,17 +997,20 @@ class SimpleSAML_Session {
* The timeout value can be SimpleSAML_Session::DATA_TIMEOUT_LOGOUT, which indicates * The timeout value can be SimpleSAML_Session::DATA_TIMEOUT_LOGOUT, which indicates
* that the data should be deleted on logout (and not before). * that the data should be deleted on logout (and not before).
* *
* @param $type The type of the data. This is checked when retrieving data from the store. * @param string $type The type of the data. This is checked when retrieving data from the store.
* @param $id The identifier of the data. * @param string $id The identifier of the data.
* @param $data The data. * @param mixed $data The data.
* @param $timeout The number of seconds this data should be stored after its last access. * @param int|NULL $timeout The number of seconds this data should be stored after its last access.
* This parameter is optional. The default value is set in 'session.datastore.timeout', * This parameter is optional. The default value is set in 'session.datastore.timeout',
* and the default is 4 hours. * and the default is 4 hours.
* @throws Exception If the data couldn't be stored.
*
*/ */
public function setData($type, $id, $data, $timeout = NULL) { public function setData($type, $id, $data, $timeout = NULL) {
assert('is_string($type)'); assert('is_string($type)');
assert('is_string($id)'); assert('is_string($id)');
assert('is_int($timeout) || is_null($timeout) || $timeout === self::DATA_TIMEOUT_LOGOUT || $timeout === self::DATA_TIMEOUT_SESSION_END'); assert('is_int($timeout) || is_null($timeout) || $timeout === self::DATA_TIMEOUT_LOGOUT ||'.
' $timeout === self::DATA_TIMEOUT_SESSION_END');
/* Clean out old data. */ /* Clean out old data. */
$this->expireData(); $this->expireData();
...@@ -1027,9 +1070,9 @@ class SimpleSAML_Session { ...@@ -1027,9 +1070,9 @@ class SimpleSAML_Session {
* Note that this will not change when the data stored in the data store will expire. If that is required, * Note that this will not change when the data stored in the data store will expire. If that is required,
* the data should be written back with setData. * the data should be written back with setData.
* *
* @param $type The type of the data. This must match the type used when adding the data. * @param string $type The type of the data. This must match the type used when adding the data.
* @param $id The identifier of the data. Can be NULL, in which case NULL will be returned. * @param string|NULL $id The identifier of the data. Can be NULL, in which case NULL will be returned.
* @return The data of the given type with the given id or NULL if the data doesn't exist in the data store. * @return mixed The data of the given type with the given id or NULL if the data doesn't exist in the data store.
*/ */
public function getData($type, $id) { public function getData($type, $id) {
assert('is_string($type)'); assert('is_string($type)');
...@@ -1066,8 +1109,8 @@ class SimpleSAML_Session { ...@@ -1066,8 +1109,8 @@ class SimpleSAML_Session {
* *
* An empty array will be returned if no data of the given type is found. * An empty array will be returned if no data of the given type is found.
* *
* @param $type The type of the data. * @param string $type The type of the data.
* @return An associative array with all data of the given type. * @return array An associative array with all data of the given type.
*/ */
public function getDataOfType($type) { public function getDataOfType($type) {
assert('is_string($type)'); assert('is_string($type)');
...@@ -1137,7 +1180,8 @@ class SimpleSAML_Session { ...@@ -1137,7 +1180,8 @@ class SimpleSAML_Session {
$globalConfig = SimpleSAML_Configuration::getInstance(); $globalConfig = SimpleSAML_Configuration::getInstance();
if ($session->authToken !== NULL) { if ($session->authToken !== NULL) {
$authTokenCookieName = $globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken'); $authTokenCookieName = $globalConfig->getString('session.authtoken.cookiename',
'SimpleSAMLAuthToken');
if (!isset($_COOKIE[$authTokenCookieName])) { if (!isset($_COOKIE[$authTokenCookieName])) {
SimpleSAML_Logger::warning('Missing AuthToken cookie.'); SimpleSAML_Logger::warning('Missing AuthToken cookie.');
return NULL; return NULL;
...@@ -1369,7 +1413,7 @@ class SimpleSAML_Session { ...@@ -1369,7 +1413,7 @@ class SimpleSAML_Session {
* This function is just for backwards-compatibility. New code should * This function is just for backwards-compatibility. New code should
* use the SimpleSAML_IdP::getAssociations()-function. * use the SimpleSAML_IdP::getAssociations()-function.
* *
* @return array Array of SAML 2 entitiyIDs. * @return array Array of SAML 2 entityIDs.
* @deprecated Will be removed in the future. * @deprecated Will be removed in the future.
*/ */
public function get_sp_list() { public function get_sp_list() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment