]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Verify certificates are valid
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 25 Aug 2021 02:11:57 +0000 (21:11 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 25 Aug 2021 17:18:42 +0000 (12:18 -0500)
Check certificate validity periods explicitly for lib/tls users and rlm_cipher

Add %(<inst>_certificate:notBefore) and %(<inst>_certificate:notAfter)

14 files changed:
raddb/mods-available/cipher
src/lib/server/module.h
src/lib/tls/all.mk
src/lib/tls/cert.c [new file with mode: 0644]
src/lib/tls/cert.h [new file with mode: 0644]
src/lib/tls/conf-h
src/lib/tls/ctx.c
src/lib/tls/log.c
src/lib/tls/log.h
src/modules/rlm_cipher/rlm_cipher.c
src/modules/rlm_ocsp/ocsp.c
src/tests/modules/cipher/fingerprint.unlang
src/tests/modules/cipher/serial.unlang
src/tests/modules/cipher/valid.unlang [new file with mode: 0644]

index acf86f28d899c4f9472cab7afb2c93f433fdf789..c2d4b1dafa510bec18243bbc8a0545ee86df9cb1 100644 (file)
 #  | `%{<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.
@@ -84,6 +88,26 @@ cipher {
                #
                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 { ... }::
                #
index c540b61f48f11d2f9fd85a948fa59851a68c294b..79a0a51f59a4f0fe76e89de02a3a01c8bf54e5c7 100644 (file)
@@ -259,8 +259,9 @@ typedef struct {
  *
  */
 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
index 00c6497b76b95fc27746560da6524da408649ef4..d90834ca48d8be9b0f1a0c5f288a2db788129658 100644 (file)
@@ -8,6 +8,7 @@ SOURCES := \
        base.c \
        bio.c \
        cache.c \
+       cert.c \
        conf.c \
        ctx.c \
        engine.c \
diff --git a/src/lib/tls/cert.c b/src/lib/tls/cert.c
new file mode 100644 (file)
index 0000000..ff5a407
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *   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(&not_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(&not_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
diff --git a/src/lib/tls/cert.h b/src/lib/tls/cert.h
new file mode 100644 (file)
index 0000000..dd57b68
--- /dev/null
@@ -0,0 +1,42 @@
+#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 */
index 8cc90f71e707aedc0e44219676fa0d0f22124398..bd0d46029b6503ea868c79a77f9f62b713a54e32 100644 (file)
@@ -72,6 +72,8 @@ typedef struct {
        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
index 47a8f6e66dfc1b933bf2612e09ab3ca20e05e81e..a71b601f5a4bb8355c4ce5752c6b8b4ee672ac87 100644 (file)
@@ -41,6 +41,7 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
 
 #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)
@@ -116,7 +117,7 @@ static int ctx_dh_params_load(SSL_CTX *ctx, char *file)
        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;
 
@@ -224,6 +225,72 @@ static int tls_ctx_load_cert_chain(SSL_CTX *ctx, fr_tls_chain_conf_t const *chai
                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, &not_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;
 
@@ -265,6 +332,7 @@ static int tls_ctx_load_cert_chain(SSL_CTX *ctx, fr_tls_chain_conf_t const *chai
                        break;
                }
        }
+
        return 0;
 }
 
@@ -667,7 +735,7 @@ SSL_CTX *fr_tls_ctx_alloc(fr_tls_conf_t const *conf, bool client)
                                        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 */
                }
index ace13a2043fa37c26937ec1d4d206d28106c8fe0..61a38f7341951ac505a94d5f1dc0bdc60e66072e 100644 (file)
@@ -75,7 +75,7 @@ static _Thread_local  fr_tls_log_bio_t        *request_log_bio;
 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];
 
@@ -83,11 +83,11 @@ static void _tls_ctx_print_cert_line(char const *file, int line,
        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);
        }
 }
 
@@ -98,18 +98,19 @@ DIAG_OFF(used-but-marked-unused)    /* fix spurious warnings for sk macros */
  * @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)
index dc6efbd84b7f0c120e002d2224c26ed4bf58f44f..93e83a2ed4f4688d99a6f1c6066e2ffc0f6659f4 100644 (file)
@@ -34,10 +34,10 @@ RCSIDH(tls_log_h, "$Id$")
 
 #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));
index 1bf93d63f70c787314c59e3eedaebc7451e6ba69..c55da0bd3bbae85ef55ec11ad285c6969e30b3d0 100644 (file)
@@ -31,6 +31,7 @@ RCSID("$Id$")
 #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>
@@ -56,6 +57,26 @@ typedef enum {
        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
  *
  */
@@ -84,6 +105,24 @@ static fr_table_num_sorted_t const cipher_type[] = {
 };
 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.
@@ -104,6 +143,8 @@ typedef struct {
        char const              *label;                         //!< Additional input to the hashing function.
 } cipher_rsa_oaep_t;
 
+
+
 /** Configuration for RSA encryption/decryption/signing
  *
  */
@@ -116,6 +157,10 @@ typedef struct {
        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.
@@ -159,6 +204,14 @@ static const CONF_PARSER rsa_config[] = {
        { 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" },
@@ -459,11 +512,36 @@ static int cipher_rsa_certificate_file_load(TALLOC_CTX *ctx, void *out, void *pa
                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 */
@@ -795,8 +873,9 @@ static xlat_action_t cipher_rsa_verify_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        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
 };
 
@@ -805,7 +884,7 @@ static xlat_arg_parser_t const cipher_fingerprint_xlat_args[] = {
  * Arguments are @verbatim(<digest>)@endverbatim
  *
 @verbatim
-%(<inst>_fingerprint:<digest>)
+%(<inst>_certificate:fingerprint <digest>)
 @endverbatim
  *
  * @ingroup xlat_functions
@@ -815,12 +894,21 @@ static xlat_action_t cipher_fingerprint_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                             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);
@@ -842,14 +930,10 @@ static xlat_action_t cipher_fingerprint_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        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
@@ -898,6 +982,39 @@ static xlat_action_t cipher_serial_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out,
        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.
@@ -1308,23 +1425,9 @@ static int mod_bootstrap(void *instance, CONF_SECTION *conf)
                                                          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,
index 3ea59bbfd09db768e93e8a93fb5c41fda41b2393..633b1dce974cf88418bfa580bdd310c0be86226d 100644 (file)
@@ -234,7 +234,7 @@ int fr_tls_ocsp_staple_cb(SSL *ssl, void *data)
        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();
        }
 
index c09b48f966d80a509afebf964b5af132d2da0ca3..1c55906b3df67a6dbe9b869ca65760b051c9de67 100644 (file)
@@ -3,7 +3,7 @@
 #  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) {
@@ -13,7 +13,7 @@ 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) {
index 085213b830cdeffa69e08bd9770b18f3741d2a3b..1168ed117d033f06e5b65b2ad5a97df8af83ac71 100644 (file)
@@ -1,5 +1,5 @@
 update request {
-       &Tmp-Octets-0 := "%(cipher_rsa_serial:)"
+       &Tmp-Octets-0 := "%(cipher_rsa_certificate:serial)"
 }
 
 if ("%(length:%{Tmp-Octets-0})" != 1) {
diff --git a/src/tests/modules/cipher/valid.unlang b/src/tests/modules/cipher/valid.unlang
new file mode 100644 (file)
index 0000000..5b44a75
--- /dev/null
@@ -0,0 +1,11 @@
+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
+}