From 6647e4ccb89f63580897b332ce030c0f68b96c12 Mon Sep 17 00:00:00 2001
From: Joost van Dijk <argine@xs4all.nl>
Date: Wed, 27 May 2015 01:07:51 +0200
Subject: [PATCH] add authentication processing filter for generating
 certificate expiry warnings

---
 .../dictionaries/X509warning.definition.json  | 11 +++
 .../dictionaries/X509warning.translation.json | 11 +++
 modules/authX509/docs/authX509.txt            | 14 ++++
 .../lib/Auth/Process/ExpiryWarning.php        | 84 +++++++++++++++++++
 modules/authX509/templates/X509warning.php    | 41 +++++++++
 modules/authX509/www/expirywarning.php        | 29 +++++++
 6 files changed, 190 insertions(+)
 create mode 100644 modules/authX509/dictionaries/X509warning.definition.json
 create mode 100644 modules/authX509/dictionaries/X509warning.translation.json
 create mode 100644 modules/authX509/lib/Auth/Process/ExpiryWarning.php
 create mode 100644 modules/authX509/templates/X509warning.php
 create mode 100644 modules/authX509/www/expirywarning.php

diff --git a/modules/authX509/dictionaries/X509warning.definition.json b/modules/authX509/dictionaries/X509warning.definition.json
new file mode 100644
index 000000000..4770dcf2c
--- /dev/null
+++ b/modules/authX509/dictionaries/X509warning.definition.json
@@ -0,0 +1,11 @@
+{
+	"warning": {
+		"en": "Your certificate will expire in %days% days. Please renew your certificate in time."
+	},
+	"warning_header": {
+		"en": "Your certificate is about to expire."
+	},
+	"proceed": {
+		"en": "Proceed"
+	}
+}
diff --git a/modules/authX509/dictionaries/X509warning.translation.json b/modules/authX509/dictionaries/X509warning.translation.json
new file mode 100644
index 000000000..11ac94b55
--- /dev/null
+++ b/modules/authX509/dictionaries/X509warning.translation.json
@@ -0,0 +1,11 @@
+{
+	"warning": {
+		"nl": "Je certificaat verloopt over %days% dagen. Vervang tijdig je certificaat."
+	},
+	"warning_header": {
+		"nl": "Je certificaat verloopt binnenkort."
+	},
+	"proceed": {
+		"nl": "Verder"
+	}
+}
diff --git a/modules/authX509/docs/authX509.txt b/modules/authX509/docs/authX509.txt
index 8cb3ff352..f37c8ee25 100644
--- a/modules/authX509/docs/authX509.txt
+++ b/modules/authX509/docs/authX509.txt
@@ -106,3 +106,17 @@ can hack your metadata/saml20-idp-hosted.php file that way:
                             'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
     )
 
+Checking certificate expiry
+===========================
+
+To issue warnings to users whos certificate is about to expire, configure an authproc filter.
+
+Example:
+
+     10 => array(
+         'class' => 'authX509:ExpiryWarning',
+         'warndaysbefore' => '30',
+     ),
+
+Parameter `warndaysbefore` specifies the number of days the user's certificate needs to be valid before a warning is
+issued. The default is 30.
\ No newline at end of file
diff --git a/modules/authX509/lib/Auth/Process/ExpiryWarning.php b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
new file mode 100644
index 000000000..f9dedf573
--- /dev/null
+++ b/modules/authX509/lib/Auth/Process/ExpiryWarning.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * Filter which shows a warning if the user's client certificate is about to expire.
+ *
+ ** <code>
+ * // show about2xpire warning if client certificate is about to expire
+ * 10 => array(
+ *     'class' => 'authX509:ExpiryWarning',
+ *     'warndaysbefore' => '30',
+ * ),
+ * </code>
+ *
+ * @author Joost van Dijk, SURFnet. <Joost.vanDijk@surfnet.nl>
+ * @package simpleSAMLphp
+ */
+class sspmod_authX509_Auth_Process_ExpiryWarning extends SimpleSAML_Auth_ProcessingFilter {
+
+    private $warndaysbefore = 30;
+
+    /**
+     * Initialize this filter.
+     *
+     * @param array $config  Configuration information about this filter.
+     * @param mixed $reserved  For future use.
+     */
+    public function __construct($config, $reserved) {
+        parent::__construct($config, $reserved);
+
+        assert('is_array($config)');
+
+        if (array_key_exists('warndaysbefore', $config)) {
+            $this->warndaysbefore = $config['warndaysbefore'];
+            if (!is_string($this->warndaysbefore)) {
+                throw new Exception('Invalid value for \'warndaysbefore\'-option to authX509::ExpiryWarning filter.');
+            }
+        }
+    }
+
+    /**
+	 * Process an authentication response.
+	 *
+	 * This function saves the state, and if necessary redirects the user to the page where the user
+	 * is informed about the expiry date of his/her certificate.
+	 *
+	 * @param array $state  The state of the response.
+	 */
+	public function process(&$state) {
+		assert('is_array($state)');
+
+		if (isset($state['isPassive']) && $state['isPassive'] === TRUE) {
+			/* We have a passive request. Skip the warning. */
+			return;
+		}
+
+        if (!isset($_SERVER['SSL_CLIENT_CERT']) ||
+            ($_SERVER['SSL_CLIENT_CERT'] == '')) {
+            return;
+        }
+
+        $client_cert = $_SERVER['SSL_CLIENT_CERT'];
+        $client_cert_data = openssl_x509_parse($client_cert);
+        if ($client_cert_data == FALSE) {
+            SimpleSAML_Logger::error('authX509: invalid cert');
+            return;
+        }
+        $validTo = $client_cert_data['validTo_time_t'];
+        $now = time();
+        $daysleft = (int)(($validTo - $now) / (24*60*60));
+        if ($daysleft > $this->warndaysbefore) {
+            /* We have a certificate that will be valid for some time. Skip the warning. */
+            return;
+        }
+
+        SimpleSAML_Logger::warning('authX509: user certificate expires in ' . $daysleft . ' days');
+        $state['daysleft'] = $daysleft;
+
+        /* Save state and redirect. */
+		$id = SimpleSAML_Auth_State::saveState($state, 'warning:expire');
+		$url = SimpleSAML_Module::getModuleURL('authX509/expirywarning.php');
+		\SimpleSAML\Utils\HTTP::redirectTrustedURL($url, array('StateId' => $id));
+	}
+
+}
diff --git a/modules/authX509/templates/X509warning.php b/modules/authX509/templates/X509warning.php
new file mode 100644
index 000000000..b391ddda2
--- /dev/null
+++ b/modules/authX509/templates/X509warning.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * Template form for X509 warnings.
+ *
+ * Parameters:
+ * - 'target': Target URL for the continue-button.
+ * - 'data': Parameters which should be included in the request.
+ *
+ * @package simpleSAMLphp
+ */
+
+$warning = $this->t('{authX509:X509warning:warning}', array(
+    '%days%' => htmlspecialchars($this->data['daysleft']),
+));
+
+$this->data['header'] = $this->t('{authX509:X509warning:warning_header}');
+$this->data['autofocus'] = 'proceedbutton';
+
+$this->includeAtTemplateBase('includes/header.php');
+
+?>
+
+<form style="display: inline; margin: 0px; padding: 0px" action="<?php echo htmlspecialchars($this->data['target']); ?>">
+
+	<?php
+		// Embed hidden fields...
+		foreach ($this->data['data'] as $name => $value) {
+			echo('<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($value) . '" />');
+		}
+	?>
+    <p><?php echo $warning; ?></p>
+
+	<input type="submit" name="proceed" id="proceedbutton" value="<?php echo htmlspecialchars($this->t('{authX509:X509warning:proceed}')) ?>" />
+
+</form>
+
+
+<?php
+
+$this->includeAtTemplateBase('includes/footer.php');
diff --git a/modules/authX509/www/expirywarning.php b/modules/authX509/www/expirywarning.php
new file mode 100644
index 000000000..f09a425cc
--- /dev/null
+++ b/modules/authX509/www/expirywarning.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * This script warns a user that his/her certificate is about to expire.
+ *
+ * @package simpleSAMLphp
+ */
+
+SimpleSAML_Logger::info('AuthX509 - Showing expiry warning to user');
+
+if (!array_key_exists('StateId', $_REQUEST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.');
+}
+$id = $_REQUEST['StateId'];
+$state = SimpleSAML_Auth_State::loadState($id, 'warning:expire');
+
+
+if (array_key_exists('proceed', $_REQUEST)) {
+	/* The user has pressed the proceed-button. */
+	SimpleSAML_Auth_ProcessingChain::resumeProcessing($state);
+}
+
+$globalConfig = SimpleSAML_Configuration::getInstance();
+
+$t = new SimpleSAML_XHTML_Template($globalConfig, 'authX509:X509warning.php');
+$t->data['target'] = SimpleSAML_Module::getModuleURL('authX509/expirywarning.php');
+$t->data['data'] = array('StateId' => $id);
+$t->data['daysleft'] = $state['daysleft'];
+$t->show();
-- 
GitLab