From 2db26fb5fd183a10a422188bbf173c09c511a9ff Mon Sep 17 00:00:00 2001
From: Andjelko Horvat <comel@vingd.com>
Date: Wed, 4 Sep 2013 11:04:21 +0000
Subject: [PATCH] Add session check function (issue #568).

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@3268 44740490-163a-0410-bde0-09ae8108e29a
---
 config-templates/config.php             |  8 ++++
 docs/simplesamlphp-advancedfeatures.txt | 50 +++++++++++++++++++++++++
 lib/SimpleSAML/Session.php              | 38 +++++++++++++++----
 3 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/config-templates/config.php b/config-templates/config.php
index 2379369c1..4a286d3fd 100644
--- a/config-templates/config.php
+++ b/config-templates/config.php
@@ -290,6 +290,14 @@ $config = array (
 	 */
 	'session.authtoken.cookiename' => 'SimpleSAMLAuthToken',
 
+	/**
+	 * Custom function for session checking called on session init and loading.
+	 * See docs/simplesamlphp-advancedfeatures.txt for function code example.
+	 *
+	 * Example:
+	 *   'session.check_function' => array('sspmod_example_Util', 'checkSession'),
+	 */
+
 	/*
 	 * Languages available, RTL languages, and what language is default
 	 */
diff --git a/docs/simplesamlphp-advancedfeatures.txt b/docs/simplesamlphp-advancedfeatures.txt
index e5bac450c..c1af38f4f 100644
--- a/docs/simplesamlphp-advancedfeatures.txt
+++ b/docs/simplesamlphp-advancedfeatures.txt
@@ -171,6 +171,56 @@ There is also an additional fallback for the private key and the certificate. If
 
 
 
+Session checking function
+-------------------------
+
+Optional session checking function, called on session init and loading, defined with 'session.check_function' in config.php.
+
+Example code for the function with GeoIP country check:
+
+
+    public static function checkSession($session, $init = FALSE) {
+        $data_type = 'example:check_session';
+        $data_key = 'remote_addr';
+
+        $remote_addr = NULL;
+        if (!empty($_SERVER['REMOTE_ADDR'])) {
+            $remote_addr = (string)$_SERVER['REMOTE_ADDR'];
+        }
+
+        if ($init) {
+            $session->setData($data_type, $data_key, $remote_addr);
+            return;
+        }
+
+        if (!function_exists('geoip_country_code_by_name')) {
+            SimpleSAML_Logger::warning('geoip php module required.');
+            return TRUE;
+        }
+
+        $stored_remote_addr = $session->getData($data_type, $data_key);
+        if ($stored_remote_addr === NULL) {
+            SimpleSAML_Logger::warning('Stored data not found.');
+            return FALSE;
+        }
+
+        $country_a = geoip_country_code_by_name($remote_addr);
+        $country_b = geoip_country_code_by_name($stored_remote_addr);
+
+        if ($country_a === $country_b) {
+            if ($stored_remote_addr !== $remote_addr) {
+                $session->setData($data_type, $data_key, $remote_addr);
+            }
+
+            return TRUE;
+        }
+
+        return FALSE;
+    }
+
+
+
+
 Support
 -------
 
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index d586e5104..e373dc4f4 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -169,6 +169,14 @@ class SimpleSAML_Session {
 
 		$this->dirty = TRUE;
 		$this->addShutdownFunction();
+
+		/* Initialize data for session check function if defined */
+		$globalConfig = SimpleSAML_Configuration::getInstance();
+		$checkFunction = $globalConfig->getArray('session.check_function', NULL);
+		if (isset($checkFunction)) {
+			assert('is_callable($checkFunction)');
+			call_user_func($checkFunction, $this, TRUE);
+		}
 	}
 
 
@@ -1030,16 +1038,30 @@ class SimpleSAML_Session {
 			$session->sessionId = $sh->getCookieSessionId();
 		}
 
-		if ($checkToken && $session->authToken !== NULL) {
+		if ($checkToken) {
 			$globalConfig = SimpleSAML_Configuration::getInstance();
-			$authTokenCookieName = $globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken');
-			if (!isset($_COOKIE[$authTokenCookieName])) {
-				SimpleSAML_Logger::warning('Missing AuthToken cookie.');
-				return NULL;
+
+			if ($session->authToken !== NULL) {
+				$authTokenCookieName = $globalConfig->getString('session.authtoken.cookiename', 'SimpleSAMLAuthToken');
+				if (!isset($_COOKIE[$authTokenCookieName])) {
+					SimpleSAML_Logger::warning('Missing AuthToken cookie.');
+					return NULL;
+				}
+				if ($_COOKIE[$authTokenCookieName] !== $session->authToken) {
+					SimpleSAML_Logger::warning('Invalid AuthToken cookie.');
+					return NULL;
+				}
 			}
-			if ($_COOKIE[$authTokenCookieName] !== $session->authToken) {
-				SimpleSAML_Logger::warning('Invalid AuthToken cookie.');
-				return NULL;
+
+			/* Run session check function if defined */
+			$checkFunction = $globalConfig->getArray('session.check_function', NULL);
+			if (isset($checkFunction)) {
+				assert('is_callable($checkFunction)');
+				$check = call_user_func($checkFunction, $session);
+				if ($check !== TRUE) {
+					SimpleSAML_Logger::warning('Session did not pass check function.');
+					return NULL;
+				}
 			}
 		}
 
-- 
GitLab