]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Move OpenSSL version handling code to its own source file
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 5 Jan 2022 01:58:55 +0000 (19:58 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 5 Jan 2022 01:58:55 +0000 (19:58 -0600)
src/bin/radiusd.c
src/bin/unit_test_module.c
src/lib/server/dependency.c
src/lib/server/dependency.h
src/lib/tls/all.mk
src/lib/tls/base-h
src/lib/tls/base.c
src/lib/tls/version.c [new file with mode: 0644]
src/lib/tls/version.h [new file with mode: 0644]

index 59241c5e5a63047ffd3ed27a4d2a5da5f5715cc7..f0e291e765ef423db0041ffd5567e3b18333283e 100644 (file)
@@ -74,8 +74,8 @@ RCSID("$Id$")
 #  include <systemd/sd-daemon.h>
 #endif
 
-#if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x30000000L
-#  include <openssl/provider.h>
+#ifdef WITH_TLS
+#  include <freeradius-devel/tls/version.h>
 #endif
 
 char const *radiusd_version = RADIUSD_VERSION_STRING_BUILD("FreeRADIUS");
@@ -485,7 +485,7 @@ int main(int argc, char *argv[])
         *  Mismatch between build time OpenSSL and linked SSL, better to die
         *  here than segfault later.
         */
-       if (ssl_check_consistency() < 0) EXIT_WITH_FAILURE;
+       if (fr_openssl_version_consistent() < 0) EXIT_WITH_FAILURE;
 
        /*
         *  Initialising OpenSSL once, here, is safer than having individual modules do it.
@@ -623,11 +623,11 @@ int main(int argc, char *argv[])
        /*
         *  Check for vulnerabilities in the version of libssl were linked against.
         */
-#if defined(WITH_TLS) && defined(ENABLE_OPENSSL_VERSION_CHECK)
+#ifdef WITH_TLS
+#  ifdef ENABLE_OPENSSL_VERSION_CHECK
        if (fr_openssl_version_check(config->allow_vulnerable_openssl) < 0) EXIT_WITH_FAILURE;
-#endif
+#  endif
 
-#ifdef WITH_TLS
        /*
         *  Toggle FIPS mode
         */
index ce9433c3ce676d87efa5866e22b681e0699c849c..c91e0b65b86ad0203201c21ecd4b2a300b6d9fa8 100644 (file)
@@ -34,6 +34,7 @@ RCSID("$Id$")
 #include <freeradius-devel/io/listen.h>
 
 #include <freeradius-devel/tls/base.h>
+#include <freeradius-devel/tls/version.h>
 
 #include <freeradius-devel/unlang/base.h>
 
@@ -643,7 +644,7 @@ int main(int argc, char *argv[])
         *  Mismatch between build time OpenSSL and linked SSL, better to die
         *  here than segfault later.
         */
-       if (ssl_check_consistency() < 0) EXIT_WITH_FAILURE;
+       if (fr_openssl_version_consistent() < 0) EXIT_WITH_FAILURE;
 
        /*
         *  Initialising OpenSSL once, here, is safer than having individual modules do it.
index 656fdc6dd690e06a16cc9bf1cd9b4f03de704ec6..42c5b89ceda6a87c58c5d39cec73a4e030289e22 100644 (file)
@@ -38,234 +38,16 @@ static CONF_SECTION *default_version_cs;           //!< Default configuration section to
 #include <freeradius-devel/tls/openssl_user_macros.h>
 
 #ifdef WITH_TLS
-#  include <freeradius-devel/tls/openssl_user_macros.h>
+#  include <freeradius-devel/tls/version.h>
 #  include <openssl/crypto.h>
 #  include <openssl/opensslv.h>
 #  include <openssl/engine.h>
+#endif
 
 #ifdef HAVE_VALGRIND_H
 #  include <valgrind.h>
 #endif
 
-static long ssl_built = OPENSSL_VERSION_NUMBER;
-
-/** Check built and linked versions of OpenSSL match
- *
- * OpenSSL version number consists of:
- * MNNFFPPS: major minor fix patch status
- *
- * Where status >= 0 && < 10 means beta, and status 10 means release.
- *
- *     https://wiki.openssl.org/index.php/Versioning
- *
- * Startup check for whether the linked version of OpenSSL matches the
- * version the server was built against.
- *
- * @return
- *     - 0 if ok.
- *     - -1 if not ok.
- */
-int ssl_check_consistency(void)
-{
-       unsigned long ssl_linked;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L
-       ssl_linked = OpenSSL_version_num();
-#else
-       ssl_linked = (unsigned long)SSLeay();
-#endif
-
-       /*
-        *      Major and minor versions mismatch, that's bad.
-        */
-       if ((ssl_linked & 0xfff00000) != (ssl_built & 0xfff00000)) goto mismatch;
-
-       /*
-        *      1.1.0 and later export all of the APIs we need, so we
-        *      don't care about mismatches in fix / patch / status
-        *      fields.  If the major && minor fields match, that's
-        *      good enough.
-        */
-       if ((ssl_linked & 0xfff00000) >= 0x10100000) return 0;
-
-       /*
-        *      Before 1.1.0, we need all kinds of stupid checks to
-        *      see if it might work.
-        */
-
-       /*
-        *      Status mismatch always triggers error.
-        */
-       if ((ssl_linked & 0x0000000f) != (ssl_built & 0x0000000f)) {
-       mismatch:
-               ERROR("libssl version mismatch.  built: %lx linked: %lx",
-                     (unsigned long) ssl_built,
-                     (unsigned long) ssl_linked);
-
-               return -1;
-       }
-
-       /*
-        *      Use the OpenSSH approach and relax fix checks after version
-        *      1.0.0 and only allow moving backwards within a patch
-        *      series.
-        */
-       if (ssl_built & 0xf0000000) {
-               if ((ssl_built & 0xfffff000) != (ssl_linked & 0xfffff000) ||
-                   (ssl_built & 0x00000ff0) > (ssl_linked & 0x00000ff0)) goto mismatch;
-       /*
-        *      Before 1.0.0 we require the same major minor and fix version
-        *      and ignore the patch number.
-        */
-       } else if ((ssl_built & 0xfffff000) != (ssl_linked & 0xfffff000)) goto mismatch;
-
-       return 0;
-}
-
-/** Convert a version number to a text string
- *
- * @note Not thread safe.
- *
- * @param v version to convert.
- * @return pointer to a static buffer containing the version string.
- */
-char const *ssl_version_by_num(uint32_t v)
-{
-       /* 2 (%s) + 1 (.) + 2 (%i) + 1 (.) + 2 (%i) + 1 (c) + 8 (%s) + \0 */
-       static char buffer[18];
-       char *p = buffer;
-
-       p += sprintf(p, "%u.%u.%u",
-                    (0xf0000000 & v) >> 28,
-                    (0x0ff00000 & v) >> 20,
-                    (0x000ff000 & v) >> 12);
-
-       if ((0x00000ff0 & v) >> 4) {
-               *p++ =  (char) (0x60 + ((0x00000ff0 & v) >> 4));
-       }
-
-       *p++ = ' ';
-
-       /*
-        *      Development (0)
-        */
-       if ((0x0000000f & v) == 0) {
-               strcpy(p, "dev");
-       /*
-        *      Beta (1-14)
-        */
-       } else if ((0x0000000f & v) <= 14) {
-               sprintf(p, "beta %u", 0x0000000f & v);
-       } else {
-               strcpy(p, "release");
-       }
-
-       return buffer;
-}
-
-/** Convert two openssl version numbers into a range string
- *
- * @note Not thread safe.
- *
- * @param low version to convert.
- * @param high version to convert.
- * @return pointer to a static buffer containing the version range string.
- */
-char const *ssl_version_range(uint32_t low, uint32_t high)
-{
-       /* 18 (version) + 3 ( - ) + 18 (version) */
-       static char buffer[40];
-       char *p = buffer;
-
-       p += strlcpy(p, ssl_version_by_num(low), sizeof(buffer));
-       p += strlcpy(p, " - ", sizeof(buffer) - (p - buffer));
-       strlcpy(p, ssl_version_by_num(high), sizeof(buffer) - (p - buffer));
-
-       return buffer;
-}
-
-#  if OPENSSL_VERSION_NUMBER >= 0x10101000L
-/** Return the linked SSL version number as a string
- *
- * @return pointer to a static buffer containing the version string.
- */
-char const *ssl_version_num(void)
-{
-       unsigned long ssl_linked;
-
-       ssl_linked = OpenSSL_version_num();
-       return ssl_version_by_num((uint32_t)ssl_linked);
-}
-
-/** Print the current linked version of Openssl
- *
- * Print the currently linked version of the OpenSSL library.
- *
- * @note Not thread safe.
- * @return pointer to a static buffer containing libssl version information.
- */
-char const *ssl_version(void)
-{
-       static char buffer[256];
-
-       unsigned long v = OpenSSL_version_num();
-
-       snprintf(buffer, sizeof(buffer), "%s 0x%.8lx (%s)",
-                OpenSSL_version(OPENSSL_VERSION),              /* Not all builds include a useful version number */
-                v,
-                ssl_version_by_num(v));
-
-       return buffer;
-}
-#  else
-/** Return the linked SSL version number as a string
- *
- * @return pointer to a static buffer containing the version string.
- */
-char const *ssl_version_num(void)
-{
-       long ssl_linked;
-
-       ssl_linked = SSLeay();
-       return ssl_version_by_num((uint32_t)ssl_linked);
-}
-
-/** Print the current linked version of Openssl
- *
- * Print the currently linked version of the OpenSSL library.
- *
- * @note Not thread safe.
- * @return pointer to a static buffer containing libssl version information.
- */
-char const *ssl_version(void)
-{
-       static char buffer[256];
-       long ssl_linked = SSLeay();
-
-       snprintf(buffer, sizeof(buffer), "%s 0x%.8x (%s)",
-                SSLeay_version(SSLEAY_VERSION),                /* Not all builds include a useful version number */
-                ssl_linked,
-                ssl_version_by_num(v));
-
-       return buffer;
-}
-#  endif
-#else
-int ssl_check_consistency(void) {
-       return 0;
-}
-
-char const *ssl_version_num(void)
-{
-       return "not linked";
-}
-
-char const *ssl_version()
-{
-       return "not linked";
-}
-#endif /* ifdef WITH_TLS */
-
 /** Check if the application linking to the library has the correct magic number
  *
  * @param magic number as defined by RADIUSD_MAGIC_NUMBER
@@ -522,7 +304,9 @@ void dependency_version_numbers_init(CONF_SECTION *cs)
        snprintf(buffer, sizeof(buffer), "%i.%i.*", talloc_version_major(), talloc_version_minor());
        dependency_version_number_add(cs, "talloc", buffer);
 
-       dependency_version_number_add(cs, "ssl", ssl_version_num());
+#if WITH_TLS
+       dependency_version_number_add(cs, "ssl", fr_openssl_version_basic());
+#endif
 
 #ifdef HAVE_REGEX
 #  ifdef HAVE_REGEX_PCRE2
index f036ecfc13452030c561393ddebb0f4ca3827a07..51e94db2f622c0b585a95275020526f7376fd875 100644 (file)
@@ -37,11 +37,6 @@ extern char const    *radiusd_version_short;
 #include <stddef.h>
 
 int            rad_check_lib_magic(uint64_t magic);
-int            ssl_check_consistency(void);
-char const     *ssl_version_by_num(uint32_t version);
-char const     *ssl_version_num(void);
-char const     *ssl_version_range(uint32_t low, uint32_t high);
-char const     *ssl_version(void);
 int            dependency_feature_add(CONF_SECTION *cs, char const *name, bool enabled);
 int            dependency_version_number_add(CONF_SECTION *cs, char const *name, char const *version);
 void           dependency_features_init(CONF_SECTION *cs) CC_HINT(nonnull);
index d90834ca48d8be9b0f1a0c5f288a2db788129658..9686a7301dfc5292346518bd2f57151bf1f65bf7 100644 (file)
@@ -17,6 +17,7 @@ SOURCES       := \
        session.c \
        utils.c \
        verify.c \
+       version.c \
        virtual_server.c
 
 TGT_PREREQS := libfreeradius-internal.a libfreeradius-util.a
index 06334b3a1510d958fe3caf486e340c8825468ed4..c518e95df63c40ec27c4ac393a9803ef572f2064 100644 (file)
@@ -156,11 +156,6 @@ SSL_CTX            *fr_tls_ctx_alloc(fr_tls_conf_t const *conf, bool client);
 /*
  *     tls/base.c
  */
-
-#ifdef ENABLE_OPENSSL_VERSION_CHECK
-int            fr_openssl_version_check(char const *acknowledged);
-#endif
-
 int            fr_openssl_thread_init(size_t async_pool_size_init, size_t async_pool_size_max);
 
 int            fr_openssl_init(void);
index 4d82af28f6a442a4392502413aae8711a9f1d544..fe5f8624e873a7cd5e410201a4abfe913befc344 100644 (file)
@@ -199,104 +199,6 @@ fr_dict_enum_autoload_t tls_dict_enum[] = {
  */
 int fr_tls_max_threads = 1;
 
-#ifdef ENABLE_OPENSSL_VERSION_CHECK
-typedef struct {
-       uint64_t        high;           //!< The last version number this defect affected.
-       uint64_t        low;            //!< The first version this defect affected.
-
-       char const      *id;            //!< CVE (or other ID)
-       char const      *name;          //!< As known in the media...
-       char const      *comment;       //!< Where to get more information.
-} fr_openssl_defect_t;
-
-#undef VM
-#undef Vm
-#define VM(_a,_b,_c) (((((_a) << 24) | ((_b) << 16) | ((_c) << 8)) << 4) | 0x0f)
-#define Vm(_a,_b,_c,_d) (((((_a) << 24) | ((_b) << 16) | ((_c) << 8) | ((_d) - 'a' + 1)) << 4) | 0x0f)
-
-/* Record critical defects in libssl here, new versions of OpenSSL to older versions of OpenSSL.  */
-static fr_openssl_defect_t fr_openssl_defects[] =
-{
-       {
-               .low            = Vm(1,1,0,'a'),                /* 1.1.0a */
-               .high           = Vm(1,1,0,'a'),                /* 1.1.0a */
-               .id             = "CVE-2016-6309",
-               .name           = "OCSP status request extension",
-               .comment        = "For more information see https://www.openssl.org/news/secadv/20160926.txt"
-       },
-       {
-               .low            = VM(1,1,0),                    /* 1.1.0  */
-               .high           = VM(1,1,0),                    /* 1.1.0  */
-               .id             = "CVE-2016-6304",
-               .name           = "OCSP status request extension",
-               .comment        = "For more information see https://www.openssl.org/news/secadv/20160922.txt"
-       }
-};
-#endif /* ENABLE_OPENSSL_VERSION_CHECK */
-
-#ifdef ENABLE_OPENSSL_VERSION_CHECK
-/** Check for vulnerable versions of libssl
- *
- * @param acknowledged The highest CVE number a user has confirmed is not present in the system's
- *     libssl.
- * @return 0 if the CVE specified by the user matches the most recent CVE we have, else -1.
- */
-int fr_openssl_version_check(char const *acknowledged)
-{
-       bool bad = false;
-       size_t i;
-       unsigned long ssl_linked;
-
-
-       /*
-        *      Didn't get passed anything, that's an error.
-        */
-       if (!acknowledged || !*acknowledged) {
-               ERROR("Refusing to start until 'allow_vulnerable_openssl' is given a value");
-               return -1;
-       }
-
-       if (strcmp(acknowledged, "yes") == 0) return 0;
-
-       /* Check for bad versions */
-
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L
-       ssl_linked = OpenSSL_version_num();
-#else
-       ssl_linked = (unsigned long)SSLeay();
-#endif
-
-       for (i = 0; i < (NUM_ELEMENTS(fr_openssl_defects)); i++) {
-               fr_openssl_defect_t *defect = &fr_openssl_defects[i];
-
-               if ((ssl_linked >= defect->low) && (ssl_linked <= defect->high)) {
-                       /*
-                        *      If the CVE is acknowledged, allow it.
-                        */
-                       if (!bad && (strcmp(acknowledged, defect->id) == 0)) return 0;
-
-                       ERROR("Refusing to start with libssl version %s (in range %s)",
-                             ssl_version(), ssl_version_range(defect->low, defect->high));
-                       ERROR("Security advisory %s (%s)", defect->id, defect->name);
-                       ERROR("%s", defect->comment);
-
-                       /*
-                        *      Only warn about the first one...
-                        */
-                       if (!bad) {
-                               INFO("Once you have verified libssl has been correctly patched, "
-                                    "set security.allow_vulnerable_openssl = '%s'", defect->id);
-                               bad = true;
-                       }
-               }
-       }
-
-       if (bad) return -1;
-
-       return 0;
-}
-#endif
-
 /** Allocate memory for OpenSSL in the NULL context
  *
  * @param len to alloc.
diff --git a/src/lib/tls/version.c b/src/lib/tls/version.c
new file mode 100644 (file)
index 0000000..45c67d1
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *   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/version.c
+ * @brief Check OpenSSL library/header consistency, and process version information.
+ *
+ * @copyright 2022 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#define LOG_PREFIX "tls"
+
+#include "version.h"
+
+#ifdef WITH_TLS
+#include <freeradius-devel/server/log.h>
+
+static long ssl_built = OPENSSL_VERSION_NUMBER;
+
+/** Check built and linked versions of OpenSSL match
+ *
+ * OpenSSL version number consists of:
+ * MNNFFPPS: major minor fix patch status
+ *
+ * Where status >= 0 && < 10 means beta, and status 10 means release.
+ *
+ *     https://wiki.openssl.org/index.php/Versioning
+ *
+ * Startup check for whether the linked version of OpenSSL matches the
+ * version the server was built against.
+ *
+ * @return
+ *     - 0 if ok.
+ *     - -1 if not ok.
+ */
+int fr_openssl_version_consistent(void)
+{
+       unsigned long ssl_linked;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+       ssl_linked = OpenSSL_version_num();
+#else
+       ssl_linked = (unsigned long)SSLeay();
+#endif
+
+       /*
+        *      Major and minor versions mismatch, that's bad.
+        *
+        *      We still allow mismatches between patch versions
+        *      as they should be ABI compatible.
+        *
+        *      This should work for >= 1.1.0 including 3.0.0
+        */
+       if ((ssl_linked & 0xfff00000) != (ssl_built & 0xfff00000)) {
+               ERROR("libssl version mismatch.  built: %lx linked: %lx",
+                     (unsigned long) ssl_built,
+                     (unsigned long) ssl_linked);
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Convert a version number to a text string
+ *
+ * @note Not thread safe.
+ *
+ * @param v version to convert.
+ * @return pointer to a static buffer containing the version string.
+ */
+char const *fr_openssl_version_str_from_num(uint32_t v)
+{
+       /* 2 (%s) + 1 (.) + 2 (%i) + 1 (.) + 2 (%i) + 1 (c) + 8 (%s) + \0 */
+       static char buffer[18];
+       char *p = buffer;
+
+       /*
+        *      If OpenSSL major version is less than three
+        *      use the old version number layout.
+        */
+       if ((v & 0xf0000000) < 3) {
+               p += sprintf(p, "%u.%u.%u",
+                            (0xf0000000 & v) >> 28,
+                            (0x0ff00000 & v) >> 20,
+                            (0x000ff000 & v) >> 12);
+
+               if ((0x00000ff0 & v) >> 4) {
+                       *p++ =  (char) (0x60 + ((0x00000ff0 & v) >> 4));
+               }
+
+               *p++ = ' ';
+
+               /*
+                *      Development (0)
+                */
+               if ((0x0000000f & v) == 0) {
+                       strcpy(p, "dev");
+               /*
+                *      Beta (1-14)
+                */
+               } else if ((0x0000000f & v) <= 14) {
+                       sprintf(p, "beta %u", 0x0000000f & v);
+               } else {
+                       strcpy(p, "release");
+               }
+
+               return buffer;
+       }
+
+       /*
+        *      If OpenSSL major version is >= 3 us the
+        *      new version number layout
+        *
+        *      OPENSSL_VERSION_NUMBER is a combination of the major, minor
+        *      and patch version into a single integer 0xMNN00PP0L, where:
+        *
+        *      M is the number from OPENSSL_VERSION_MAJOR, in hexadecimal notation.
+        *      NN is the number from OPENSSL_VERSION_MINOR, in hexadecimal notation.
+        *      PP is the number from OPENSSL_VERSION_PATCH, in hexadecimal notation.
+        */
+       sprintf(buffer, "%u.%u.%u",
+               (0xf0000000 & v) >> 28,
+               (0x0ff00000 & v) >> 20,
+               (0x00000ff0 & v) >> 4);
+
+       return buffer;
+}
+
+/** Convert two openssl version numbers into a range string
+ *
+ * @param[in] low version to convert.
+ * @param[in] high version to convert.
+ * @return pointer to a static buffer containing the version range string.
+ */
+char const *fr_openssl_version_range(uint32_t low, uint32_t high)
+{
+       /* 18 (version) + 3 ( - ) + 18 (version) */
+       static _Thread_local char buffer[40];
+       char *p = buffer;
+
+       p += strlcpy(p, fr_openssl_version_str_from_num(low), sizeof(buffer));
+       p += strlcpy(p, " - ", sizeof(buffer) - (p - buffer));
+       strlcpy(p, fr_openssl_version_str_from_num(high), sizeof(buffer) - (p - buffer));
+
+       return buffer;
+}
+
+#  if OPENSSL_VERSION_NUMBER >= 0x10101000L
+/** Return the linked SSL version number as a string
+ *
+ * @return pointer to a static buffer containing the version string.
+ */
+char const *fr_openssl_version_basic(void)
+{
+       unsigned long ssl_linked;
+
+       ssl_linked = OpenSSL_version_num();
+       return fr_openssl_version_str_from_num((uint32_t)ssl_linked);
+}
+
+/** Print the current linked version of Openssl
+ *
+ * Print the currently linked version of the OpenSSL library.
+ *
+ * @return pointer to a static buffer containing libssl version information.
+ */
+char const *fr_openssl_version_expanded(void)
+{
+       static _Thread_local char buffer[256];
+
+       unsigned long v = OpenSSL_version_num();
+
+       snprintf(buffer, sizeof(buffer), "%s 0x%.8lx (%s)",
+                OpenSSL_version(OPENSSL_VERSION),              /* Not all builds include a useful version number */
+                v,
+                fr_openssl_version_str_from_num(v));
+
+       return buffer;
+}
+#  else
+/** Return the linked SSL version number as a string
+ *
+ * @return pointer to a static buffer containing the version string.
+ */
+char const *fr_openssl_version_basic(void)
+{
+       long ssl_linked;
+
+       ssl_linked = SSLeay();
+       return fr_openssl_version_str_from_num((uint32_t)ssl_linked);
+}
+
+/** Print the current linked version of Openssl
+ *
+ * Print the currently linked version of the OpenSSL library.
+ *
+ * @note Not thread safe.
+ *
+ * @return pointer to a static buffer containing libssl version information.
+ */
+char const *fr_openssl_version_expanded(void)
+{
+       static _Thread_local char buffer[256];
+       long ssl_linked = SSLeay();
+
+       snprintf(buffer, sizeof(buffer), "%s 0x%.8x (%s)",
+                SSLeay_version(SSLEAY_VERSION),                /* Not all builds include a useful version number */
+                ssl_linked,
+                fr_openssl_version_str_from_num(v));
+
+       return buffer;
+}
+#  endif
+
+#  ifdef ENABLE_OPENSSL_VERSION_CHECK
+typedef struct {
+       uint64_t        high;           //!< The last version number this defect affected.
+       uint64_t        low;            //!< The first version this defect affected.
+
+       char const      *id;            //!< CVE (or other ID)
+       char const      *name;          //!< As known in the media...
+       char const      *comment;       //!< Where to get more information.
+} fr_openssl_defect_t;
+
+#  undef VM
+#  undef Vm
+#  define VM(_a,_b,_c) (((((_a) << 24) | ((_b) << 16) | ((_c) << 8)) << 4) | 0x0f)
+#  define Vm(_a,_b,_c,_d) (((((_a) << 24) | ((_b) << 16) | ((_c) << 8) | ((_d) - 'a' + 1)) << 4) | 0x0f)
+
+/* Record critical defects in libssl here, new versions of OpenSSL to older versions of OpenSSL.  */
+static fr_openssl_defect_t fr_openssl_defects[] =
+{
+       {
+               .low            = Vm(1,1,0,'a'),                /* 1.1.0a */
+               .high           = Vm(1,1,0,'a'),                /* 1.1.0a */
+               .id             = "CVE-2016-6309",
+               .name           = "OCSP status request extension",
+               .comment        = "For more information see https://www.openssl.org/news/secadv/20160926.txt"
+       },
+       {
+               .low            = VM(1,1,0),                    /* 1.1.0  */
+               .high           = VM(1,1,0),                    /* 1.1.0  */
+               .id             = "CVE-2016-6304",
+               .name           = "OCSP status request extension",
+               .comment        = "For more information see https://www.openssl.org/news/secadv/20160922.txt"
+       }
+};
+
+/** Check for vulnerable versions of libssl
+ *
+ * @param acknowledged The highest CVE number a user has confirmed is not present in the system's
+ *     libssl.
+ * @return 0 if the CVE specified by the user matches the most recent CVE we have, else -1.
+ */
+int fr_openssl_version_check(char const *acknowledged)
+{
+       bool bad = false;
+       size_t i;
+       unsigned long ssl_linked;
+
+
+       /*
+        *      Didn't get passed anything, that's an error.
+        */
+       if (!acknowledged || !*acknowledged) {
+               ERROR("Refusing to start until 'allow_vulnerable_openssl' is given a value");
+               return -1;
+       }
+
+       if (strcmp(acknowledged, "yes") == 0) return 0;
+
+       /* Check for bad versions */
+
+#    if OPENSSL_VERSION_NUMBER >= 0x10101000L
+       ssl_linked = OpenSSL_version_num();
+#    else
+       ssl_linked = (unsigned long)SSLeay();
+#    endif
+
+       for (i = 0; i < (NUM_ELEMENTS(fr_openssl_defects)); i++) {
+               fr_openssl_defect_t *defect = &fr_openssl_defects[i];
+
+               if ((ssl_linked >= defect->low) && (ssl_linked <= defect->high)) {
+                       /*
+                        *      If the CVE is acknowledged, allow it.
+                        */
+                       if (!bad && (strcmp(acknowledged, defect->id) == 0)) return 0;
+
+                       ERROR("Refusing to start with libssl version %s (in range %s)",
+                             fr_openssl_version_expanded(), fr_openssl_version_range(defect->low, defect->high));
+                       ERROR("Security advisory %s (%s)", defect->id, defect->name);
+                       ERROR("%s", defect->comment);
+
+                       /*
+                        *      Only warn about the first one...
+                        */
+                       if (!bad) {
+                               INFO("Once you have verified libssl has been correctly patched, "
+                                    "set security.allow_vulnerable_openssl = '%s'", defect->id);
+                               bad = true;
+                       }
+               }
+       }
+
+       if (bad) return -1;
+
+       return 0;
+}
+#  endif
+#else
+int fr_openssl_version_consistent(void) {
+       return 0;
+}
+
+char const *fr_openssl_version_basic(void)
+{
+       return "not linked";
+}
+
+char const *fr_openssl_version_expanded(void)
+{
+       return "not linked";
+}
+#endif /* ifdef WITH_TLS */
diff --git a/src/lib/tls/version.h b/src/lib/tls/version.h
new file mode 100644 (file)
index 0000000..083768d
--- /dev/null
@@ -0,0 +1,53 @@
+#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
+ */
+/**
+ * $Id$
+ *
+ * @file lib/tls/version.h
+ * @brief Structures for dealing with OpenSSL library versions
+ *
+ * @copyright 2022 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSIDH(tls_version_h, "$Id$")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "openssl_user_macros.h"
+
+#ifdef WITH_TLS
+#  include <openssl/ssl.h>
+#endif
+
+/*
+ *     If we're not building with TLS, dummy functions will
+ *     be provided.
+ */
+int            fr_openssl_version_consistent(void);
+char const     *fr_openssl_version_str_from_num(uint32_t version);
+char const     *fr_openssl_version_basic(void);
+char const     *fr_openssl_version_range(uint32_t low, uint32_t high);
+char const     *fr_openssl_version_expanded(void);
+
+#ifdef ENABLE_OPENSSL_VERSION_CHECK
+int            fr_openssl_version_check(char const *acknowledged);
+#endif
+
+#ifdef __cplusplus
+}
+#endif