diff --git a/config-templates/config.php b/config-templates/config.php index 461a9f0c1799f93db02938e3306d87b7f691c0ea..e95e60ab7236b2803304875dd5bc6866d37837b3 100644 --- a/config-templates/config.php +++ b/config-templates/config.php @@ -169,6 +169,10 @@ $config = array ( */ 'session.datastore.timeout' => (4*60*60), // 4 hours + /* + * Option to override the default settings for the session cookie name + */ + 'session.cookie.name' => NULL, /* * Expiration time for the session cookie, in seconds. @@ -209,6 +213,19 @@ $config = array ( */ 'session.cookie.secure' => FALSE, + /* + * Enable secure POST from HTTPS to HTTP. + * + * If you have some SP's on HTTP and IdP is normally on HTTPS, this option + * enables secure POSTing to HTTP endpoint without warning from browser. + * + * For this to work, module.php/core/postredirect.php must be accessible + * also via HTTP on IdP, e.g. if your IdP is on + * https://idp.example.org/ssp/, then + * http://idp.example.org/ssp/module.php/core/postredirect.php must be accessible. + */ + 'enable.http_post' => FALSE, + /* * Options to override the default settings for php sessions. */ diff --git a/lib/SimpleSAML/SessionHandlerCookie.php b/lib/SimpleSAML/SessionHandlerCookie.php index 0aca668a598d72dd5abd6f2fbe930368fde33dd0..f69d060b18879d1d5fd7c8cf0aa7ea06b0b1f4b6 100644 --- a/lib/SimpleSAML/SessionHandlerCookie.php +++ b/lib/SimpleSAML/SessionHandlerCookie.php @@ -17,9 +17,12 @@ abstract class SimpleSAML_SessionHandlerCookie extends SimpleSAML_SessionHandler { /* This variable contains the current session id. */ - protected $session_id = NULL; + private $session_id = NULL; + /* This variable contains the session cookie name. */ + protected $cookie_name; + /* This constructor initializes the session id based on what * we receive in a cookie. We create a new session id and set @@ -31,27 +34,8 @@ extends SimpleSAML_SessionHandler { */ parent::__construct(); - /* Attempt to retrieve the session id from the cookie. */ - if(array_key_exists('SimpleSAMLSessionID', $_COOKIE)) { - $this->session_id = $_COOKIE['SimpleSAMLSessionID']; - } - - /* We need to create a new session. */ - - if (headers_sent()) { - throw new SimpleSAML_Error_Exception('Cannot create new session - headers already sent.'); - } - - /* Check if we have a valid session id. */ - if(self::isValidSessionID($this->session_id)) { - /* We are done now if it was valid. */ - return; - } - - /* We don't have a valid session. Create a new session id. */ - $this->session_id = self::createSessionID(); - - $this->setCookie('SimpleSAMLSessionID', $this->session_id); + $config = SimpleSAML_Configuration::getInstance(); + $this->cookie_name = $config->getString('session.cookie.name', 'SimpleSAMLSessionID'); } @@ -61,6 +45,21 @@ extends SimpleSAML_SessionHandler { * @return string The session id saved in the cookie. */ public function getCookieSessionId() { + if ($this->session_id === NULL) { + if(self::hasSessionCookie()) { + /* Attempt to retrieve the session id from the cookie. */ + $this->session_id = $_COOKIE[$this->cookie_name]; + } + + /* Check if we have a valid session id. */ + if(!self::isValidSessionID($this->session_id)) { + /* We don't have a valid session. Create a new session id. */ + $this->session_id = self::createSessionID(); + } + + $this->setCookie($this->cookie_name, $this->session_id); + } + return $this->session_id; } @@ -112,7 +111,7 @@ extends SimpleSAML_SessionHandler { */ public function hasSessionCookie() { - return array_key_exists('SimpleSAMLSessionID', $_COOKIE); + return array_key_exists($this->cookie_name, $_COOKIE); } } diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php index c6c029ef06a057741a3f3d52a9dc8aec20f870e7..02188aecadf7efd93fb3784e43fb9db800a21ec6 100644 --- a/lib/SimpleSAML/SessionHandlerPHP.php +++ b/lib/SimpleSAML/SessionHandlerPHP.php @@ -49,8 +49,24 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler { if(!empty($savepath)) { session_save_path($savepath); } + } + } + + + /** + * Retrieve the session id of saved in the session cookie. + * + * @return string The session id saved in the cookie. + */ + public function getCookieSessionId() { + if(session_id() === '') { + $session_cookie_params = session_get_cookie_params(); + + if ($session_cookie_params['secure'] && !SimpleSAML_Utilities::isHTTPS()) { + throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.'); + } - if(!array_key_exists(session_name(), $_COOKIE)) { + if(!self::hasSessionCookie()) { if (headers_sent()) { throw new SimpleSAML_Error_Exception('Cannot create new session - headers already sent.'); @@ -62,15 +78,7 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler { session_start(); } - } - - /** - * Retrieve the session id of saved in the session cookie. - * - * @return string The session id saved in the cookie. - */ - public function getCookieSessionId() { return session_id(); } @@ -95,8 +103,21 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler { public function loadSession($sessionId = NULL) { assert('is_string($sessionId) || is_null($sessionId)'); - if ($sessionId !== NULL && $sessionId !== session_id()) { - throw new SimpleSAML_Error_Exception('Cannot load PHP session with a specific ID.'); + if ($sessionId !== NULL) { + if (session_id() === '') { + /* session not initiated with getCookieSessionId(), start session without setting cookie */ + $ret = ini_set('session.use_cookies', '0'); + if ($ret === FALSE) { + throw new SimpleSAML_Error_Exception('Disabling PHP option session.use_cookies failed.'); + } + + session_id($sessionId); + session_start(); + } elseif ($sessionId !== session_id()) { + throw new SimpleSAML_Error_Exception('Cannot load PHP session with a specific ID.'); + } + } elseif (session_id() === '') { + $sessionId = self::getCookieSessionId(); } if (!isset($_SESSION['SimpleSAMLphp_SESSION'])) { diff --git a/lib/SimpleSAML/SessionHandlerStore.php b/lib/SimpleSAML/SessionHandlerStore.php index 81be78ddcf5e86a1d4e48dcae5590c3cfd16a76c..cd01a5adf81bd025f56f9cb48c5b06dd2a03acaf 100644 --- a/lib/SimpleSAML/SessionHandlerStore.php +++ b/lib/SimpleSAML/SessionHandlerStore.php @@ -33,7 +33,7 @@ class SimpleSAML_SessionHandlerStore extends SimpleSAML_SessionHandlerCookie { assert('is_string($sessionId) || is_null($sessionId)'); if ($sessionId === NULL) { - $sessionId = $this->session_id; + $sessionId = $this->getCookieSessionId(); } $session = $this->store->get('session', $sessionId); @@ -47,7 +47,7 @@ class SimpleSAML_SessionHandlerStore extends SimpleSAML_SessionHandlerCookie { } /* For backwards compatibility, check the MemcacheStore object. */ - $store = SimpleSAML_MemcacheStore::find($this->session_id); + $store = SimpleSAML_MemcacheStore::find($sessionId); if ($store === NULL) { return NULL; } diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 5a38cd60bf992d36f7ccedeb427917e26d54488e..b633c2e0dd3ff64095d896f9d3f48f9a68070b85 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -1606,6 +1606,13 @@ class SimpleSAML_Utilities { assert('is_array($post)'); $config = SimpleSAML_Configuration::getInstance(); + $httpRedirect = $config->getBoolean('enable.http_post', FALSE); + + if ($httpRedirect && preg_match("#^http:#", $destination) && self::isHTTPS()) { + $url = self::createHttpPostRedirectLink($destination, $post); + self::redirect($url); + assert('FALSE'); + } $p = new SimpleSAML_XHTML_Template($config, 'post.php'); $p->data['destination'] = $destination; @@ -1625,16 +1632,54 @@ class SimpleSAML_Utilities { assert('is_string($destination)'); assert('is_array($post)'); - $id = SimpleSAML_Utilities::generateID(); + $config = SimpleSAML_Configuration::getInstance(); + $httpRedirect = $config->getBoolean('enable.http_post', FALSE); + + if ($httpRedirect && preg_match("#^http:#", $destination) && self::isHTTPS()) { + $url = self::createHttpPostRedirectLink($destination, $post); + } else { + $postId = SimpleSAML_Utilities::generateID(); + $postData = array( + 'post' => $post, + 'url' => $destination, + ); + + $session = SimpleSAML_Session::getInstance(); + $session->setData('core_postdatalink', $postId, $postData); + + $url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirId' => $postId)); + } + + return $url; + } + + + /** + * Create a link which will POST data to HTTP in a secure way. + * + * @param string $destination The destination URL. + * @param array $post The name-value pairs which will be posted to the destination. + * @return string An URL which can be accessed to post the data. + */ + public static function createHttpPostRedirectLink($destination, $post) { + assert('is_string($destination)'); + assert('is_array($post)'); + + $postId = SimpleSAML_Utilities::generateID(); $postData = array( 'post' => $post, 'url' => $destination, ); $session = SimpleSAML_Session::getInstance(); - $session->setData('core_postdatalink', $id, $postData); + $session->setData('core_postdatalink', $postId, $postData); + + $redirInfo = base64_encode(self::aesEncrypt($session->getSessionId() . ':' . $postId)); + + $url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirInfo' => $redirInfo)); + $url = preg_replace("#^https:#", "http:", $url); - return SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirId' => $id)); + return $url; } @@ -2060,4 +2105,75 @@ class SimpleSAML_Utilities { return $data; } + + /** + * Function to AES encrypt data. + * + * @param string $clear Data to encrypt. + * @return array The encrypted data and IV. + */ + public static function aesEncrypt($clear) { + assert('is_string($clear)'); + + if (!function_exists("mcrypt_encrypt")) { + throw new Exception("aesEncrypt needs mcrypt php module."); + } + + $enc = MCRYPT_RIJNDAEL_256; + $mode = MCRYPT_MODE_CBC; + + $blockSize = mcrypt_get_block_size($enc, $mode); + $ivSize = mcrypt_get_iv_size($enc, $mode); + $keySize = mcrypt_get_key_size($enc, $mode); + + $key = hash('sha256', self::getSecretSalt(), TRUE); + $key = substr($key, 0, $keySize); + + $len = strlen($clear); + $numpad = $blockSize - ($len % $blockSize); + $clear = str_pad($clear, $len + $numpad, chr($numpad)); + + $iv = self::generateRandomBytes($ivSize); + + $data = mcrypt_encrypt($enc, $key, $clear, $mode, $iv); + + return $iv . $data; + } + + + /** + * Function to AES decrypt data. + * + * @param $data Encrypted data. + * @param $iv IV of encrypted data. + * @return string The decrypted data. + */ + public static function aesDecrypt($encData) { + assert('is_string($encData)'); + + if (!function_exists("mcrypt_encrypt")) { + throw new Exception("aesDecrypt needs mcrypt php module."); + } + + $enc = MCRYPT_RIJNDAEL_256; + $mode = MCRYPT_MODE_CBC; + + $ivSize = mcrypt_get_iv_size($enc, $mode); + $keySize = mcrypt_get_key_size($enc, $mode); + + $key = hash('sha256', self::getSecretSalt(), TRUE); + $key = substr($key, 0, $keySize); + + $iv = substr($encData, 0, $ivSize); + $data = substr($encData, $ivSize); + + $clear = mcrypt_decrypt($enc, $key, $data, $mode, $iv); + + $len = strlen($clear); + $numpad = ord($clear[$len - 1]); + $clear = substr($clear, 0, $len - $numpad); + + return $clear; + } + } diff --git a/modules/core/www/postredirect.php b/modules/core/www/postredirect.php index 408fe554e65682e339af3120db0f34a5c81eaa7b..fd9bef458ebedf22dcd1b78badbf3884e8e837a3 100644 --- a/modules/core/www/postredirect.php +++ b/modules/core/www/postredirect.php @@ -7,26 +7,49 @@ * @version $Id$ */ -if (!array_key_exists('RedirId', $_REQUEST)) { - throw new SimpleSAML_Error_BadRequest('Missing RedirId parameter.'); +if (array_key_exists('RedirId', $_REQUEST)) { + $postId = $_REQUEST['RedirId']; + $session = SimpleSAML_Session::getInstance(); +} elseif (array_key_exists('RedirInfo', $_REQUEST)) { + $encData = base64_decode($_REQUEST['RedirInfo']); + + if (empty($encData)) { + throw new SimpleSAML_Error_BadRequest('Invalid RedirInfo data.'); + } + + list($sessionId, $postId) = explode(':', SimpleSAML_Utilities::aesDecrypt($encData)); + + if (empty($sessionId) || empty($postId)) { + throw new SimpleSAML_Error_BadRequest('Invalid session info data.'); + } + + $session = SimpleSAML_Session::getSession($sessionId); +} else { + throw new SimpleSAML_Error_BadRequest('Missing redirection info parameter.'); } -$id = $_REQUEST['RedirId']; +if ($session === NULL) { + throw new Exception('Unable to load session.'); +} -$session = SimpleSAML_Session::getInstance(); -$postData = $session->getData('core_postdatalink', $id); +$postData = $session->getData('core_postdatalink', $postId); if ($postData === NULL) { /* The post data is missing, probably because it timed out. */ throw new Exception('The POST data we should restore was lost.'); } + +$session->deleteData('core_postdatalink', $postId); + assert('is_array($postData)'); assert('array_key_exists("url", $postData)'); assert('array_key_exists("post", $postData)'); -$url = $postData['url']; -$post = $postData['post']; - -SimpleSAML_Utilities::postRedirect($url, $post); +$config = SimpleSAML_Configuration::getInstance(); +$p = new SimpleSAML_XHTML_Template($config, 'post.php'); +$p->data['destination'] = $postData['url']; +$p->data['post'] = $postData['post']; +$p->show(); +exit(0); ?> \ No newline at end of file