# | `%{<inst>_decrypt:<ciphertext>...}` | Decrypts ciphertext using `private_key_file`
# | `%{<inst>_sign:<plaintext>...}` | Signs plaintext using `private_key_file`
# | `%{<inst>_verify:<signature> <plaintext>...}` | Validates a signature using `certificate_file`
+# | `%(<inst>_certificate:serial)` | Returns the serial of `certificate_file`
+# | `%(<inst>_certificate:fingerprint <hash>)` | Produces a fingerprint of `certificate_file` using the specified hash.
+# | `%(<inst>_certificate:not_before)` | Retrieves the notBefore time from `certificate_file`.
+# | `%(<inst>_certificate:not_after)` | Retrieves the notAfter time from `certificate_file`.
# |===
#
# NOTE: `<ciphertext>` and `<signature>` are ingested and excreted to in their raw form.
#
certificate_file = ${certdir}/rsa/server.pem
+ #
+ # verify_mode:: How we verify certificate_file on startup
+ #
+ # After reading the certificate file from disk and parsing it we can
+ # apply other checks to ensure it is valid. Currently we check
+ # the `notBefore` and `notAfter` fields to ensure the certificate is
+ # temporally valid. Key use checks may be added in future.
+ #
+ # [options="header,autowidth"]
+ # |===
+ # | Error | Description
+ # | hard | Error out if the certificate is not yet valid or has expired.
+ # | soft | Warn if the certificate is not yet valid or has expired.
+ # | none | Stay silent if the certificate is not yet valid.
+ # |===
+ #
+ # The default is `hard`.
+ #
+# verify_mode = "hard"
+
#
# oaep { ... }::
#
*
*/
struct module_ctx_s {
- void *instance; //!< Global instance data for the module.
- void *thread; //!< Thread specific instance data.
+ void *instance; //!< Global instance data for the module.
+ void *thread; //!< Thread specific instance data.
+ void *rctx; //!< Resume ctx that a module previously set.
};
/** @name Convenience wrappers around other internal APIs to make them easier to instantiate with modules
base.c \
bio.c \
cache.c \
+ cert.c \
conf.c \
ctx.c \
engine.c \
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file tls/cert.c
+ * @brief Functions to work with certificates.
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSID("$Id$")
+USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
+
+#ifdef WITH_TLS
+#define LOG_PREFIX "tls - "
+
+#include <openssl/ssl.h>
+
+#include <freeradius-devel/util/strerror.h>
+
+#include "cert.h"
+#include "utils.h"
+
+/** Check if a certificate is currently valid
+ *
+ * @param[out] not_before_p Where to write the not before time. May be NULL.
+ * @param[out] not_after_p Where to write the not after time. May be NULL.
+ * @parma[in] cert The Certificate to validate.
+ * @return
+ * - -1 if we can't parse the notBefore or notAfter values in the cert.
+ * - -2 if the cert has expired (not_before_p, not_after_p still populated).
+ * - -3 if the cert is not yet valid (not_before_p, not_after_t still populated).
+ */
+int fr_tls_cert_is_valid(fr_unix_time_t *not_before_p, fr_unix_time_t *not_after_p, X509 *cert)
+{
+ fr_time_t now = fr_time();
+ time_t not_before, not_after;
+
+ /*
+ * If the cert has a mangled notAfter or
+ * notBefore timestamps then always fail,
+ * no matter what the verify mode.
+ */
+ if (fr_tls_utils_asn1time_to_epoch(¬_after, X509_get0_notAfter(cert)) < 0) {
+ fr_strerror_const_push("Failed parsing notAfter time in certificate");
+ return -1;
+ }
+ if (fr_tls_utils_asn1time_to_epoch(¬_before, X509_get0_notBefore(cert)) < 0) {
+ fr_strerror_const_push("Failed parsing notBefore time in certificate");
+ return -1;
+ }
+
+ if (not_before_p) *not_before_p = fr_unix_time_from_sec(not_before);
+ if (not_after_p) *not_after_p = fr_unix_time_from_sec(not_after);
+
+ /*
+ * Check the cert hasn't expired
+ */
+ if (fr_time_from_sec(not_after) < now) {
+ fr_strerror_printf("Certificate has expired. "
+ "Validity period (notAfter) ends %pV, current time is %pV",
+ fr_box_date(fr_unix_time_from_sec(not_before)), fr_box_date(now));
+ return -2;
+ }
+
+ /*
+ * Check the cert's validity period
+ * has started.
+ */
+ if (fr_time_from_sec(not_before) > now) {
+ fr_strerror_printf("Certificate is not yet valid. "
+ "Validity period (notBefore) starts %pV, current time is %pV",
+ fr_box_date(fr_unix_time_from_sec(not_before)), fr_box_date(now));
+ return -3;
+ }
+
+ return 0;
+}
+#endif
--- /dev/null
+#pragma once
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#ifdef WITH_TLS
+/**
+ * $Id$
+ *
+ * @file lib/tls/cert.h
+ * @brief Functions to work with certificates.
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSIDH(cert_h, "$Id$")
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include <freeradius-devel/util/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int fr_tls_cert_is_valid(fr_unix_time_t *not_before_p, fr_unix_time_t *not_after_p, X509 *cert);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* WITH_TLS */
fr_tls_chain_verify_mode_t verify_mode; //!< How hard we try to build up a complete certificate
///< chain.
bool include_root_ca; //!< Include the root ca in the chain we built.
+
+ fr_unix_time_t valid_until; //!< The certificate in the chain which expires the earliest.
} fr_tls_chain_conf_t;
/** Control what types of session resumption we allow
#include "base.h"
#include "log.h"
+#include "cert.h"
#ifndef OPENSSL_NO_ECDH
static int ctx_ecdh_curve_set(SSL_CTX *ctx, char const *ecdh_curve, bool disable_single_dh_use)
return 0;
}
-static int tls_ctx_load_cert_chain(SSL_CTX *ctx, fr_tls_chain_conf_t const *chain)
+static int tls_ctx_load_cert_chain(SSL_CTX *ctx, fr_tls_chain_conf_t *chain)
{
char *password;
return -1;
}
+ /*
+ * Loop over the certificates checking validity periods.
+ * SSL_CTX_build_cert_chain does this too, but we can
+ * produce significantly better errors here.
+ *
+ * After looping over all the certs we figure out when
+ * the chain will next need refreshing.
+ */
+ {
+ fr_unix_time_t expires_first = 0;
+ int ret;
+
+ for (ret = SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST);
+ ret == 1;
+ ret = SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)) {
+ fr_unix_time_t not_after;
+ STACK_OF(X509) *our_chain;
+ X509 *our_cert;
+
+ our_cert = SSL_CTX_get0_certificate(ctx);
+ if (!SSL_CTX_get0_chain_certs(ctx, &our_chain)) {
+ fr_tls_log_error(NULL, "Failed retrieving chain certificates");
+ return -1;
+ }
+
+ switch (fr_tls_cert_is_valid(NULL, ¬_after, our_cert)) {
+ case -1:
+ fr_tls_log_certificate_chain(NULL, L_ERR, our_chain, our_cert);
+ PERROR("Malformed certificate");
+ return -1;
+
+ case -2:
+ case -3:
+ switch (chain->verify_mode) {
+ case FR_TLS_CHAIN_VERIFY_SOFT:
+ fr_tls_log_certificate_chain(NULL, L_WARN, our_chain, our_cert);
+ PWARN("Certificate validation failed");
+ break;
+
+ case FR_TLS_CHAIN_VERIFY_HARD:
+ fr_tls_log_certificate_chain(NULL, L_ERR, our_chain, our_cert);
+ PERROR("Certificate validation failed");
+ return -1;
+
+ default:
+ break;
+ }
+
+ }
+
+ /*
+ * Record the time the first certificate in
+ * the chain expires so we can use it for
+ * runtime checks.
+ */
+ if ((expires_first == 0) || (expires_first > not_after)) expires_first = not_after;
+ }
+
+ /*
+ * Record this as a unix timestamp as
+ * internal time might not progress at
+ * the same rate as wallclock time.
+ */
+ chain->valid_until = expires_first;
+ }
+
{
int mode = SSL_BUILD_CHAIN_FLAG_CHECK;
break;
}
}
+
return 0;
}
goto error;
}
- if (DEBUG_ENABLED3) fr_tls_log_certificate_chain(NULL, our_chain, our_cert);
+ if (DEBUG_ENABLED3) fr_tls_log_certificate_chain(NULL, L_DBG, our_chain, our_cert);
}
(void)SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); /* Reset */
}
static _Thread_local fr_tls_log_bio_t *global_log_bio;
static void _tls_ctx_print_cert_line(char const *file, int line,
- request_t *request, int index, X509 *cert)
+ request_t *request, fr_log_type_t log_type, int idx, X509 *cert)
{
char subject[1024];
subject[sizeof(subject) - 1] = '\0';
if (request) {
- log_request(L_DBG, fr_debug_lvl, request, file, line,
- "[%i] %s %s", index, fr_tls_utils_x509_pkey_type(cert), subject);
+ log_request(log_type, fr_debug_lvl, request, file, line,
+ "[%i] %s %s", idx, fr_tls_utils_x509_pkey_type(cert), subject);
} else {
- fr_log(LOG_DST, fr_debug_lvl, file, line,
- "[%i] %s %s", index, fr_tls_utils_x509_pkey_type(cert), subject);
+ fr_log(LOG_DST, log_type, file, line,
+ "[%i] %s %s", idx, fr_tls_utils_x509_pkey_type(cert), subject);
}
}
* @param[in] file File where this function is being called.
* @param[in] line Line where this function is being called.
* @param[in] request Current request, may be NULL.
+ * @param[in] log_type The type of log message to produce L_INFO, L_ERR, L_DBG etc...
* @param[in] chain The certificate chain.
* @param[in] cert The leaf certificate.
*/
void _fr_tls_log_certificate_chain(char const *file, int line,
- request_t *request, STACK_OF(X509) *chain, X509 *cert)
+ request_t *request, fr_log_type_t log_type, STACK_OF(X509) *chain, X509 *cert)
{
int i;
for (i = sk_X509_num(chain); i > 0 ; i--) {
- _tls_ctx_print_cert_line(file, line, request, i, sk_X509_value(chain, i - 1));
+ _tls_ctx_print_cert_line(file, line, request, log_type, i, sk_X509_value(chain, i - 1));
}
- _tls_ctx_print_cert_line(file, line, request, i, cert);
+ _tls_ctx_print_cert_line(file, line, request, log_type, i, cert);
}
DIAG_ON(used-but-marked-unused)
DIAG_ON(DIAG_UNKNOWN_PRAGMAS)
#include "base.h"
-#define fr_tls_log_certificate_chain(_request, _chain, _cert) \
- _fr_tls_log_certificate_chain( __FILE__, __LINE__, _request, _chain, _cert)
+#define fr_tls_log_certificate_chain(_request, _log_type, _chain, _cert) \
+ _fr_tls_log_certificate_chain( __FILE__, __LINE__, _request, _log_type, _chain, _cert)
void _fr_tls_log_certificate_chain(char const *file, int line,
- request_t *request, STACK_OF(X509) *chain, X509 *cert);
+ request_t *request, fr_log_type_t log_type, STACK_OF(X509) *chain, X509 *cert);
int fr_tls_log_io_error(request_t *request, fr_tls_session_t *session, int ret, char const *msg, ...)
CC_HINT(format (printf, 4, 5));
#include <freeradius-devel/server/module.h>
#include <freeradius-devel/tls/base.h>
#include <freeradius-devel/tls/log.h>
+#include <freeradius-devel/tls/cert.h>
#include <freeradius-devel/util/debug.h>
#include <openssl/crypto.h>
RLM_CIPHER_TYPE_RSA = 1,
} cipher_type_t;
+/** Certificate validation modes
+ *
+ */
+typedef enum {
+ CIPHER_CERT_VERIFY_INVALID = 0,
+
+ CIPHER_CERT_VERIFY_HARD, //!< Fail if the certificate isn't valid.
+ CIPHER_CERT_VERIFY_SOFT, //!< Warn if the certificate isn't valid.
+ CIPHER_CERT_VERIFY_NONE //!< Don't check to see if the we're between
+ ///< notBefore or notAfter.
+} cipher_cert_verify_mode_t;
+
+typedef enum {
+ CIPHER_CERT_ATTR_UNKNOWN = 0, //!< Unrecognised attribute.
+ CIPHER_CERT_ATTR_SERIAL, //!< Certificate's serial number.
+ CIPHER_CERT_ATTR_FINGERPRINT, //!< Dynamically calculated fingerprint.
+ CIPHER_CERT_ATTR_NOT_BEFORE, //!< Time the certificate becomes valid.
+ CIPHER_CERT_ATTR_NOT_AFTER //!< Time the certificate expires.
+} cipher_cert_attributes_t;
+
/** Public key types
*
*/
};
static size_t cipher_type_len = NUM_ELEMENTS(cipher_type);
+static fr_table_num_sorted_t const cipher_cert_verify_mode_table[] = {
+ { L("hard"), CIPHER_CERT_VERIFY_HARD },
+ { L("none"), CIPHER_CERT_VERIFY_SOFT },
+ { L("soft"), CIPHER_CERT_VERIFY_NONE }
+};
+static size_t cipher_cert_verify_mode_table_len = NUM_ELEMENTS(cipher_cert_verify_mode_table);
+
+/** Public key types
+ *
+ */
+static fr_table_num_sorted_t const cert_attributes[] = {
+ { L("fingerprint"), CIPHER_CERT_ATTR_FINGERPRINT },
+ { L("notAfter"), CIPHER_CERT_ATTR_NOT_AFTER },
+ { L("notBefore"), CIPHER_CERT_ATTR_NOT_BEFORE },
+ { L("serial"), CIPHER_CERT_ATTR_SERIAL },
+};
+static size_t cert_attributes_len = NUM_ELEMENTS(cert_attributes);
+
typedef struct {
EVP_PKEY_CTX *evp_encrypt_ctx; //!< Pre-allocated evp_pkey_ctx.
EVP_PKEY_CTX *evp_sign_ctx; //!< Pre-allocated evp_pkey_ctx.
char const *label; //!< Additional input to the hashing function.
} cipher_rsa_oaep_t;
+
+
/** Configuration for RSA encryption/decryption/signing
*
*/
EVP_PKEY *certificate_file; //!< Public (certificate) file.
X509 *x509_certificate_file; //!< Needed for extracting certificate attributes.
+ cipher_cert_verify_mode_t verify_mode; //!< How hard we try to verify the certificate.
+ fr_unix_time_t not_before; //!< Certificate isn't valid before this time.
+ fr_unix_time_t not_after; //!< Certificate isn't valid after this time.
+
int padding; //!< Type of padding to apply to the plaintext
///< or ciphertext before feeding it to RSA crypto
///< functions.
{ FR_CONF_OFFSET("private_key_file", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, private_key_file), .func = cipher_rsa_private_key_file_load },
{ FR_CONF_OFFSET("certificate_file", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, certificate_file), .func = cipher_rsa_certificate_file_load },
+ { FR_CONF_OFFSET("verify_mode", FR_TYPE_VOID, cipher_rsa_t, verify_mode),
+ .func = cf_table_parse_int,
+ .uctx = &(cf_table_parse_ctx_t){
+ .table = cipher_cert_verify_mode_table,
+ .len = &cipher_cert_verify_mode_table_len
+ },
+ .dflt = "hard" },
+
{ FR_CONF_OFFSET("random_file", FR_TYPE_STRING, cipher_rsa_t, random_file) },
{ FR_CONF_OFFSET("signature_digest", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, cipher_rsa_t, sig_digest), .func = digest_type_parse, .dflt = "sha256" },
cf_log_err(ci, "Expected certificate to contain %s public key, got %s public key",
fr_table_str_by_value(pkey_types, EVP_PKEY_RSA, "?Unknown?"),
fr_table_str_by_value(pkey_types, pkey_type, "?Unknown?"));
-
+ error:
EVP_PKEY_free(pkey);
return -1;
}
+ /*
+ * Certificate validity checks
+ */
+ switch (fr_tls_cert_is_valid(&rsa_inst->not_before, &rsa_inst->not_after, cert)) {
+ case -1:
+
+ cf_log_perr(ci, "Malformed certificate");
+ return -1;
+
+ case -2:
+ case -3:
+ switch (rsa_inst->verify_mode) {
+ case CIPHER_CERT_VERIFY_SOFT:
+ cf_log_pwarn(ci, "Certificate validation failed");
+ break;
+
+ case CIPHER_CERT_VERIFY_HARD:
+ cf_log_perr(ci, "Certificate validation failed");
+ goto error;
+
+ default:
+ break;
+ }
+ }
+
talloc_set_type(pkey, EVP_PKEY);
(void)talloc_steal(cert, pkey); /* Bind lifetime to config */
talloc_set_destructor(pkey, _evp_pkey_free); /* Free pkey correctly on chunk free */
return XLAT_ACTION_DONE;
}
-static xlat_arg_parser_t const cipher_fingerprint_xlat_args[] = {
+static xlat_arg_parser_t const cipher_certificate_xlat_args[] = {
{ .required = true, .concat = false, .single = true, .variadic = false, .type = FR_TYPE_STRING },
+ { .required = false, .concat = false, .single = true, .variadic = false, .type = FR_TYPE_STRING }, /* Optional hash for fingerprint mode */
XLAT_ARG_PARSER_TERMINATOR
};
* Arguments are @verbatim(<digest>)@endverbatim
*
@verbatim
-%(<inst>_fingerprint:<digest>)
+%(<inst>_certificate:fingerprint <digest>)
@endverbatim
*
* @ingroup xlat_functions
fr_value_box_list_t *in)
{
rlm_cipher_t const *inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst), rlm_cipher_t);
- char const *md_name = ((fr_value_box_t *)fr_dlist_head(in))->vb_strvalue;
+ char const *md_name;
EVP_MD const *md;
size_t md_len;
fr_value_box_t *vb;
uint8_t *digest;
+ if (!fr_dlist_next(in, fr_dlist_head(in))) {
+ REDEBUG("Missing digest argument");
+ return XLAT_ACTION_FAIL;
+ }
+
+ /*
+ * Second arg...
+ */
+ md_name = ((fr_value_box_t *)fr_dlist_next(in, fr_dlist_head(in)))->vb_strvalue;
md = EVP_get_digestbyname(md_name);
if (!md) {
REDEBUG("Specified digest \"%s\" is not a valid digest type", md_name);
return XLAT_ACTION_DONE;
}
-static xlat_arg_parser_t const cipher_serial_xlat_args[] = {
- XLAT_ARG_PARSER_TERMINATOR
-};
-
/** Return the serial of the public certificate
*
@verbatim
-%(<inst>_serial:)
+%(<inst>_certificate:serial)
@endverbatim
*
* @ingroup xlat_functions
return XLAT_ACTION_DONE;
}
+static xlat_action_t cipher_certificate_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
+ request_t *request, void const *xlat_inst, void *xlat_thread_inst,
+ fr_value_box_list_t *in)
+{
+ rlm_cipher_t const *inst = talloc_get_type_abort_const(*((void const * const *)xlat_inst), rlm_cipher_t);
+ char const *attribute = ((fr_value_box_t *)fr_dlist_head(in))->vb_strvalue;
+ fr_value_box_t *vb;
+
+ switch (fr_table_value_by_str(cert_attributes, attribute, CIPHER_CERT_ATTR_UNKNOWN)) {
+ default:
+ REDEBUG("Unknown certificate attribute \"%s\"", attribute);
+ return XLAT_ACTION_FAIL;
+
+ case CIPHER_CERT_ATTR_FINGERPRINT:
+ return cipher_fingerprint_xlat(ctx, out, request, xlat_inst, xlat_thread_inst, in);
+
+ case CIPHER_CERT_ATTR_SERIAL:
+ return cipher_serial_xlat(ctx, out, request, xlat_inst, xlat_thread_inst, in);
+
+ case CIPHER_CERT_ATTR_NOT_BEFORE:
+ MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL, true));
+ vb->vb_date = inst->rsa->not_before;
+ fr_dcursor_append(out, vb);
+ return XLAT_ACTION_DONE;
+
+ case CIPHER_CERT_ATTR_NOT_AFTER:
+ MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_DATE, NULL, true));
+ vb->vb_date = inst->rsa->not_after;
+ fr_dcursor_append(out, vb);
+ return XLAT_ACTION_DONE;
+ }
+}
+
/** Talloc destructor for freeing an EVP_PKEY_CTX
*
* @param[in] evp_pkey_ctx to free.
inst);
talloc_free(xlat_name);
- xlat_name = talloc_asprintf(inst, "%s_fingerprint", inst->xlat_name);
- xlat = xlat_register(inst, xlat_name, cipher_fingerprint_xlat, false);
- xlat_func_args(xlat, cipher_fingerprint_xlat_args);
- xlat_async_instantiate_set(xlat, cipher_xlat_instantiate,
- rlm_cipher_t *,
- NULL,
- inst);
- xlat_async_thread_instantiate_set(xlat,
- cipher_xlat_thread_instantiate,
- rlm_cipher_rsa_thread_inst_t *,
- NULL,
- inst);
- talloc_free(xlat_name);
-
- xlat_name = talloc_asprintf(inst, "%s_serial", inst->xlat_name);
- xlat = xlat_register(inst, xlat_name, cipher_serial_xlat, false);
- xlat_func_args(xlat, cipher_serial_xlat_args);
+ xlat_name = talloc_asprintf(inst, "%s_certificate", inst->xlat_name);
+ xlat = xlat_register(inst, xlat_name, cipher_certificate_xlat, false);
+ xlat_func_args(xlat, cipher_certificate_xlat_args);
xlat_async_instantiate_set(xlat, cipher_xlat_instantiate,
rlm_cipher_t *,
NULL,
if (RDEBUG_ENABLED3) {
RDEBUG3("Current SSL session cert store contents");
RINDENT();
- fr_tls_log_certificate_chain(request, our_chain, cert);
+ fr_tls_log_certificate_chain(request, L_DBG, our_chain, cert);
REXDENT();
}
# but we can test the digest length, and for smoke...
#
update request {
- &Tmp-Octets-0 := "%(cipher_rsa_fingerprint:sha1)"
+ &Tmp-Octets-0 := "%(cipher_rsa_certificate:fingerprint sha1)"
}
if ("%(length:%{Tmp-Octets-0})" != 20) {
}
update request {
- &Tmp-Octets-0 := "%(cipher_rsa_fingerprint:sha256)"
+ &Tmp-Octets-0 := "%(cipher_rsa_certificate:fingerprint sha256)"
}
if ("%(length:%{Tmp-Octets-0})" != 32) {
update request {
- &Tmp-Octets-0 := "%(cipher_rsa_serial:)"
+ &Tmp-Octets-0 := "%(cipher_rsa_certificate:serial)"
}
if ("%(length:%{Tmp-Octets-0})" != 1) {
--- /dev/null
+update request {
+ &Tmp-Date-0 := "%(cipher_rsa_certificate:notBefore)"
+ &Tmp-Date-1 := "%(cipher_rsa_certificate:notAfter)"
+}
+
+# Check the cert validity period is 60 days
+if (<uint32>"%{expr:%(integer:%{Tmp-Date-1}) - %(integer:%{Tmp-Date-0})}" != <uint32>"%{expr:86400 * 60}") {
+ test_fail
+} else {
+ test_pass
+}