diff --git a/modules/authX509/dictionaries/X509warning.definition.json b/modules/authX509/dictionaries/X509warning.definition.json new file mode 100644 index 0000000000000000000000000000000000000000..4770dcf2c34175b7835eda6dbe09ce68b981644d --- /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 0000000000000000000000000000000000000000..11ac94b55ae76fb7113f032220c761d8cf48ef03 --- /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 8cb3ff352fe044ccd0e4a1d7bdf0f009e1afc188..f37c8ee2574a571ac8df2851b9b4eebdc55789ec 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 0000000000000000000000000000000000000000..f9dedf573693ad2f81e331c2bf1cb724ebb520a6 --- /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 0000000000000000000000000000000000000000..b391ddda2368b2b9256d728d98aedc8990a5beb5 --- /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 0000000000000000000000000000000000000000..f09a425ccdaf6f2c0806eadd623dcec73f954b1e --- /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();