From f896a8756016a4f7b0864d7d75407e6f467c6383 Mon Sep 17 00:00:00 2001 From: Arran Cudbard-Bell Date: Mon, 11 Feb 2019 09:56:52 +0530 Subject: [PATCH] Expose more TLS options in MySQL driver Availability of TLS and reconnect options in MySQL driver checked by using MySQL version instead of ifdefs --- .../drivers/rlm_sql_mysql/rlm_sql_mysql.c | 103 ++++++++++++++++-- 1 file changed, 96 insertions(+), 7 deletions(-) diff --git a/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c b/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c index 2b05db8182f..1be5ebf36ba 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c @@ -45,6 +45,20 @@ RCSID("$Id$") # include #endif +#if (MYSQL_VERSION_ID >= 50555) && (MYSQL_VERSION_ID < 50600) +#define HAVE_TLS_OPTIONS 1 +#define HAVE_CRL_OPTIONS 0 +#define HAVE_TLS_VERIFY_OPTIONS 0 +#elif (MYSQL_VERSION_ID >= 50636) && (MYSQL_VERSION_ID < 50700) +#define HAVE_TLS_OPTIONS 1 +#define HAVE_CRL_OPTIONS 1 +#define HAVE_TLS_VERIFY_OPTIONS 0 +#elif MYSQL_VERSION_ID >= 50700 +#define HAVE_TLS_OPTIONS 1 +#define HAVE_CRL_OPTIONS 1 +#define HAVE_TLS_VERIFY_OPTIONS 1 +#endif + #include "rlm_sql.h" static int mysql_instance_count = 0; @@ -69,14 +83,24 @@ typedef struct rlm_sql_mysql_conn { } rlm_sql_mysql_conn_t; typedef struct rlm_sql_mysql_config { - char const *tls_ca_file; //!< Path to the CA used to validate the server's certificate. - char const *tls_ca_path; //!< Directory containing CAs that may be used to validate the + char const *tls_ca_file; //!< Path to the CA used to validate the server's certificate. + char const *tls_ca_path; //!< Directory containing CAs that may be used to validate the //!< servers certificate. - char const *tls_certificate_file; //!< Public certificate we present to the server. - char const *tls_private_key_file; //!< Private key for the certificate we present to the server. - char const *tls_cipher; + char const *tls_certificate_file; //!< Public certificate we present to the server. + char const *tls_private_key_file; //!< Private key for the certificate we present to the server. + + char const *tls_crl_file; //!< Public certificate we present to the server. + char const *tls_crl_path; //!< Private key for the certificate we present to the server. + + char const *tls_cipher; //!< Colon separated list of TLS ciphers for TLS <= 1.2. - char const *warnings_str; //!< Whether we always query the server for additional warnings. + bool tls_required; //!< Require that the connection is encrypted. + bool tls_check_cert; //!< Verify there's a trust relationship between the server's + ///< cert and one of the CAs we have configured. + bool tls_check_cert_cn; //!< Verify that the CN in the server cert matches the host + ///< we passed to mysql_real_connect(). + + char const *warnings_str; //!< Whether we always query the server for additional warnings. rlm_sql_mysql_warnings warnings; //!< mysql_warning_count() doesn't //!< appear to work with NDB cluster } rlm_sql_mysql_config_t; @@ -87,10 +111,30 @@ static CONF_PARSER tls_config[] = { { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_certificate_file), NULL }, { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_private_key_file), NULL }, +#if HAVE_CRL_OPTIONS + { "crl_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_crl_file), NULL }, + { "crl_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_crl_path), NULL }, +#endif /* * MySQL Specific TLS attributes */ { "cipher", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_mysql_config_t, tls_cipher), NULL }, + + /* + * The closest thing we have to these options in other modules is + * in rlm_rest. rlm_ldap has its own bizarre option set. + * + * There, the options can be toggled independently, here they can't + * but for consistency we break them out anyway, and warn if the user + * has provided an invalid list of flags. + */ +#if HAVE_TLS_OPTIONS + { "tls_required", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_mysql_config_t, tls_required), "no" }, +# if HAVE_TLS_VERIFY_OPTIONS + { "check_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_mysql_config_t, tls_check_cert), "no" }, + { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_mysql_config_t, tls_check_cert_cn), "no" }, +# endif +#endif CONF_PARSER_TERMINATOR }; @@ -119,6 +163,23 @@ static int _mod_destructor(UNUSED rlm_sql_mysql_config_t *driver) { if (--mysql_instance_count == 0) mysql_library_end(); +#if HAVE_TLS_VERIFY_OPTIONS + if (driver->tls_check_cert && !driver->tls_required) { + WARN("Implicitly setting tls_required = yes, as tls_check_cert = yes"); + driver->tls_required = true; + } + if (driver->tls_check_cert_cn) { + if (!driver->tls_required) { + WARN("Implicitly setting tls_required = yes, as check_cert_cn = yes"); + driver->tls_required = true; + } + + if (!driver->tls_check_cert) { + WARN("Implicitly setting check_cert = yes, as check_cert_cn = yes"); + driver->tls_check_cert = true; + } + } +#endif return 0; } @@ -186,13 +247,41 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c driver->tls_ca_file, driver->tls_ca_path, driver->tls_cipher); } +#if HAVE_TLS_OPTIONS + { + unsigned int ssl_mode = 0; + bool ssl_mode_isset = false; + + if (driver->tls_required) { + ssl_mode = SSL_MODE_REQUIRED; + ssl_mode_isset = true; + } +# if HAVE_TLS_VERIFY_OPTIONS + if (driver->tls_check_cert) { + ssl_mode = SSL_MODE_VERIFY_CA; + ssl_mode_isset = true; + } + if (driver->tls_check_cert_cn) { + ssl_mode = SSL_MODE_VERIFY_IDENTITY; + ssl_mode_isset = true; + } +# endif + if (ssl_mode_isset) mysql_options(&(conn->db), MYSQL_OPT_SSL_MODE, &ssl_mode); + } +#endif + +#if HAVE_CRL_OPTIONS + if (driver->tls_crl_file) mysql_options(&(conn->db), MYSQL_OPT_SSL_CRL, driver->tls_crl_file); + if (driver->tls_crl_path) mysql_options(&(conn->db), MYSQL_OPT_SSL_CRLPATH, driver->tls_crl_path); +#endif + mysql_options(&(conn->db), MYSQL_READ_DEFAULT_GROUP, "freeradius"); /* * We need to know about connection errors, and are capable * of reconnecting automatically. */ -#ifdef MYSQL_OPT_RECONNECT +#if MYSQL_VERSION_ID >= 50013 { int reconnect = 0; mysql_options(&(conn->db), MYSQL_OPT_RECONNECT, &reconnect); -- 2.47.3