From: Alan T. DeKok Date: Mon, 29 Nov 2021 13:39:32 +0000 (-0500) Subject: add SSLKEYLOGFILE capability X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f87c1abe0a4e7ad8dfd6a32781b6e9e3e31c0e2;p=thirdparty%2Ffreeradius-server.git add SSLKEYLOGFILE capability and add "keylog_file" to the TLS configuration, so that we can distinguish SSL keys for EAP versus incoming / outgoing RadSec --- diff --git a/src/lib/tls/conf-h b/src/lib/tls/conf-h index f52a32b6540..43d1073bf5c 100644 --- a/src/lib/tls/conf-h +++ b/src/lib/tls/conf-h @@ -170,6 +170,8 @@ struct fr_tls_conf_s { char const *psk_query; #endif + char const *keylog_file; //!< for SSLKEYLOGFILE functionality. + fr_tls_cache_conf_t cache; //!< Session cache configuration. fr_tls_verify_conf_t verify; }; diff --git a/src/lib/tls/conf.c b/src/lib/tls/conf.c index 65f8643d321..2fda62cc90b 100644 --- a/src/lib/tls/conf.c +++ b/src/lib/tls/conf.c @@ -170,6 +170,8 @@ CONF_PARSER fr_tls_server_config[] = { { FR_CONF_OFFSET("psk_hexphrase", FR_TYPE_STRING | FR_TYPE_SECRET, fr_tls_conf_t, psk_password) }, { FR_CONF_OFFSET("psk_query", FR_TYPE_STRING, fr_tls_conf_t, psk_query) }, #endif + { FR_CONF_OFFSET("keylog_file", FR_TYPE_STRING, fr_tls_conf_t, keylog_file) }, + { FR_CONF_OFFSET("dh_file", FR_TYPE_FILE_INPUT, fr_tls_conf_t, dh_file) }, { FR_CONF_OFFSET("fragment_size", FR_TYPE_UINT32, fr_tls_conf_t, fragment_size), .dflt = "1024" }, { FR_CONF_OFFSET("padding", FR_TYPE_UINT32, fr_tls_conf_t, padding_block_size), }, @@ -208,6 +210,8 @@ CONF_PARSER fr_tls_client_config[] = { { FR_CONF_DEPRECATED("private_key_password", FR_TYPE_STRING | FR_TYPE_SECRET, fr_tls_conf_t, NULL) }, { FR_CONF_DEPRECATED("private_key_file", FR_TYPE_FILE_INPUT, fr_tls_conf_t, NULL) }, + { FR_CONF_OFFSET("keylog_file", FR_TYPE_STRING, fr_tls_conf_t, keylog_file) }, + { FR_CONF_OFFSET("verify_depth", FR_TYPE_UINT32, fr_tls_conf_t, verify_depth), .dflt = "0" }, { FR_CONF_OFFSET("ca_path", FR_TYPE_FILE_INPUT, fr_tls_conf_t, ca_path) }, diff --git a/src/lib/tls/ctx.c b/src/lib/tls/ctx.c index e433ab60e6a..6a8a1688442 100644 --- a/src/lib/tls/ctx.c +++ b/src/lib/tls/ctx.c @@ -1028,6 +1028,15 @@ post_ca: */ if (fr_tls_cache_ctx_init(ctx, &conf->cache) < 0) goto error; +#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) + /* + * Set the keylog file if the admin requested it. + */ + if ((getenv("SSLKEYLOGFILE") != NULL) || (conf->keylog_file && *conf->keylog_file)) { + SSL_CTX_set_keylog_callback(ctx, fr_tls_session_keylog_cb); + } +#endif + return ctx; } #endif diff --git a/src/lib/tls/session.c b/src/lib/tls/session.c index 0776c3240ab..9fe64a5c7db 100644 --- a/src/lib/tls/session.c +++ b/src/lib/tls/session.c @@ -42,6 +42,7 @@ #include #include +#include #include "attrs.h" #include "base.h" @@ -841,6 +842,82 @@ void fr_tls_session_msg_cb(int write_p, int msg_version, int content_type, #endif } +#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) +/* + * By setting the environment variable SSLKEYLOGFILE to a filename keying + * material will be exported that you may use with Wireshark to decode any + * TLS flows. Please see the following for more details: + * + * https://gitlab.com/wireshark/wireshark/-/wikis/TLS#tls-decryption + * + * An example logging session is (you should delete the file on each run): + * + * rm -f /tmp/sslkey.log; env SSLKEYLOGFILE=/tmp/sslkey.log freeradius -X | tee /tmp/debug + * + * Note that we rely on the OS to check file permissions. If the + * caller can run radiusd, then they can only write to files which + * they own. If radiusd is running as root, then only root can + * change the environment variables for radiusd. + * + * Note also that we don't try anything fancy, like xlat expansions. + * Those could block, and the OpenSSL API doesn't support async key + * log callbacks. Instead, + */ +void fr_tls_session_keylog_cb(const SSL *ssl, const char *line) +{ + int fd; + size_t len; + const char *filename; + char buffer[64 + 2*SSL3_RANDOM_SIZE + 2*SSL_MAX_MASTER_KEY_LENGTH]; + + /* + * Just a double-check. + */ + filename = getenv("SSLKEYLOGFILE"); + if (!filename) { + fr_tls_conf_t *conf; + + conf = (fr_tls_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF); + if (!conf || !conf->keylog_file || !*conf->keylog_file) return; + + filename = conf->keylog_file; + } + + /* + * We write all of the data at once. This is *generally* + * atomic on most systems. + */ + len = strlen(line); + if ((len + 1) > sizeof(buffer)) { + DEBUG("SSLKEYLOGFILE buffer not large enough, max %lu, required %lu", sizeof(buffer), len + 1); + return; + } + + /* + * Add a \n, which means that in order to write() the + * buffer AND the trailing \n, we have to place both + * strings into the same buffer. + */ + memcpy(buffer, line, len); + buffer[len] = '\n'; + + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); + if (fd < 0) { + DEBUG("Failed to open file %s: %s", filename, strerror(errno)); + return; + } + + /* + * This should work in most situations. + */ + if (write(fd, buffer, len + 1) < 0) { + DEBUG("Failed to write to file %s: %s", filename, strerror(errno)); + } + + close(fd); +} +#endif + /** Decrypt application data * * @note Handshake must have completed before this function may be called. diff --git a/src/lib/tls/session.h b/src/lib/tls/session.h index 176d0dbd38d..396a0943dde 100644 --- a/src/lib/tls/session.h +++ b/src/lib/tls/session.h @@ -284,6 +284,8 @@ void fr_tls_session_info_cb(SSL const *s, int where, int ret); void fr_tls_session_msg_cb(int write_p, int msg_version, int content_type, void const *buf, size_t len, SSL *ssl, void *arg); +void fr_tls_session_keylog_cb(const SSL *ssl, const char *line); + int fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, request_t *request, X509 *cert) CC_HINT(nonnull);