diff --git a/modules/authX509/dictionaries/X509warning.definition.json b/modules/authX509/dictionaries/X509warning.definition.json new file mode 100644 index 0000000000000000000000000000000000000000..e74d3077517bc60b78b8ff54d9f4baef80114fa4 --- /dev/null +++ b/modules/authX509/dictionaries/X509warning.definition.json @@ -0,0 +1,17 @@ +{ + "warning": { + "en": "Your certificate will expire in %days% days." + }, + "warning_header": { + "en": "Your certificate is about to expire." + }, + "renew": { + "en": "Please renew your certificate in time." + }, + "renew_url": { + "en": "Please <a href='%renewurl%'>renew<\/a> your certificate in time." +}, + "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..9f42e72d39f5fc8d715a96c4da23c445eb97a91c --- /dev/null +++ b/modules/authX509/dictionaries/X509warning.translation.json @@ -0,0 +1,32 @@ +{ + "warning": { + "nl": "Je certificaat verloopt over %days% dagen.", + "no": "Sertifikatet ditt vil utløpe om %days% dager.", + "da": "Dit certifikat udløber om %days% dage.", + "es": "Su certificado caduca en %days% dĂas." + }, + "warning_header": { + "nl": "Je certificaat verloopt binnenkort.", + "no": "Sertifikatet ditt vil snart utløpe.", + "da": "Dit certifikat udløber om kort tid", + "es": "Su certificado está a punto de caducar." + }, + "renew": { + "nl": "Vervang tijdig je certificaat.", + "no": "Vennligst forny sertifikatet ditt før det utløper.", + "da": "Forny venligst dit certifikat i tide.", + "es": "Por favor, renueve su certificado a tiempo." + }, + "renew_url": { + "nl": "<a href='%renewurl%'>Vervang<\/a> tijdig je certificaat.", + "no": "Vennligst <a href=â€%renewurl%’>forny<\/a> sertifikatet ditt før det utløper.", + "da": "<a href='%renewurl%'>Forny<\/a>, venligst dit certifikat før det udløber.", + "es": "Por favor, <a href=â€%renewurl%’>renueve<\/a> su certificado a tiempo." + }, + "proceed": { + "nl": "Verder", + "no": "Fortsett", + "da": "Fortsæt", + "es": "Continuar" + } +} diff --git a/modules/authX509/docs/authX509.txt b/modules/authX509/docs/authX509.txt index 8cb3ff352fe044ccd0e4a1d7bdf0f009e1afc188..0f3d12621ebf35e5eab41d73e82684e9616d2053 100644 --- a/modules/authX509/docs/authX509.txt +++ b/modules/authX509/docs/authX509.txt @@ -106,3 +106,21 @@ 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', + 'renewurl' => 'https://myca.com/renew', + ), + +Parameter `warndaysbefore` specifies the number of days the user's certificate needs to be valid before a warning is +issued. The default is 30. + +Parameter `renewurl` specifies the URL of your Certification Authority. If specified, the user is suggested to renew the +certificate immediately. diff --git a/modules/authX509/lib/Auth/Process/ExpiryWarning.php b/modules/authX509/lib/Auth/Process/ExpiryWarning.php new file mode 100644 index 0000000000000000000000000000000000000000..259228e77062ac5531d0e62ce81c84208992dd5a --- /dev/null +++ b/modules/authX509/lib/Auth/Process/ExpiryWarning.php @@ -0,0 +1,93 @@ +<?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; + private $renewurl = null; + + /** + * 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.'); + } + } + + if (array_key_exists('renewurl', $config)) { + $this->renewurl = $config['renewurl']; + if (!is_string($this->renewurl)) { + throw new Exception('Invalid value for \'renewurl\'-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; + $state['renewurl'] = $this->renewurl; + + /* 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..496775065adcf52bca2be4486f0d65bf361fc924 --- /dev/null +++ b/modules/authX509/templates/X509warning.php @@ -0,0 +1,48 @@ +<?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']), +)); + +if( $this->data['renewurl']) { + $warning .= " " . $this->t('{authX509:X509warning:renew_url}', array( + '%renewurl%' => $this->data['renewurl'], + )); +} else { + $warning .= " " . $this->t('{authX509:X509warning:renew}'); +} + +$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..200b76c9f3c10cf36214e381af26eeab2f8ff96b --- /dev/null +++ b/modules/authX509/www/expirywarning.php @@ -0,0 +1,30 @@ +<?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->data['renewurl'] = $state['renewurl']; +$t->show();