From: Arran Cudbard-Bell Date: Thu, 16 Dec 2021 18:04:02 +0000 (-0600) Subject: Allow for persistent TLS session keys X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=921e3f964ebdc3e960a04915fc3984d5e71600e9;p=thirdparty%2Ffreeradius-server.git Allow for persistent TLS session keys --- diff --git a/raddb/mods-available/eap b/raddb/mods-available/eap index b40f5f8538..fdbf88b88e 100644 --- a/raddb/mods-available/eap +++ b/raddb/mods-available/eap @@ -818,6 +818,28 @@ eap { # # require_perfect_forward_secrecy = no + # + # session_ticket_key:: + # + # Sets a persistent key used to encrypt stateless session + # tickets. If this is not set, then a random key will be + # chosen when the server starts. + * + # As the ticket key length depends on the version/flavour + # of OpenSSL being used, the value provided is fed into + # a HKDF function (digest SHA256, + # label "freeradius-session-ticket"). The number of + # bytes OpenSSL indicates it needs are then extracted from + # the HKDF. + # + # It is important that a strong key is chosen here. If the + # key were ever revealed, then an attacker could manipulate + # the contents of a session ticket. This could in turn + # allow privilege escalation, or if OpenSSL's ticket parsing + # code is less than perfect, buffer overflow attacks. + # +# session_ticket_key = "super-secret-key" + # # [NOTE] # ==== diff --git a/src/lib/tls/cache.c b/src/lib/tls/cache.c index a63af0bcf6..0ad715c7bc 100644 --- a/src/lib/tls/cache.c +++ b/src/lib/tls/cache.c @@ -30,6 +30,7 @@ USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ #define LOG_PREFIX "tls" #include +#include #include #include @@ -1190,29 +1191,96 @@ int fr_tls_cache_ctx_init(SSL_CTX *ctx, fr_tls_cache_conf_t const *cache_conf) FALL_THROUGH; case FR_TLS_CACHE_STATELESS: + { + size_t key_len; + uint8_t *key_buff; + EVP_PKEY_CTX *pkey_ctx = NULL; + if (!(cache_conf->mode & FR_TLS_CACHE_STATEFUL)) tls_cache_disable_statefull_resumption(ctx); + /* + * If keys is NULL, then OpenSSL returns the expected + * key length, which may be different across diferent + * flavours/versions of OpenSSL. + * + * We could calculate this in conf.c, but, if in future + * OpenSSL decides to use different key lengths based + * on other parameters in the ctx, that'd break. + */ + key_len = SSL_CTX_set_tlsext_ticket_keys(ctx, NULL, 0); + + if (unlikely((pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL)) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed initialising KDF"); + kdf_error: + if (pkey_ctx) EVP_PKEY_CTX_free(pkey_ctx); + return -1; + } + if (unlikely(EVP_PKEY_derive_init(pkey_ctx) != 1)) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed initialising KDF derivation ctx"); + goto kdf_error; + } + if (unlikely(EVP_PKEY_CTX_set_hkdf_md(pkey_ctx, UNCONST(struct evp_md_st *, EVP_sha256())) != 1)) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed setting KDF MD"); + goto kdf_error; + } + if (unlikely(EVP_PKEY_CTX_set1_hkdf_key(pkey_ctx, + UNCONST(unsigned char *, cache_conf->session_ticket_key), + talloc_array_length(cache_conf->session_ticket_key)) != 1)) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed setting KDF key"); + goto kdf_error; + } + if (unlikely(EVP_PKEY_CTX_add1_hkdf_info(pkey_ctx, + (unsigned char *)"freeradius-session-ticket", + sizeof("freeradius-session-ticket") - 1) != 1)) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed setting KDF label"); + goto kdf_error; + } + + /* + * SSL_CTX_set_tlsext_ticket_keys memcpys its + * inputs so this is just a temporary buffer. + */ + MEM(key_buff = talloc_array(NULL, uint8_t, key_len)); + if (EVP_PKEY_derive(pkey_ctx, key_buff, &key_len) != 1) { + fr_tls_log_strerror_printf(NULL); + PERROR("Failed deriving session ticket key"); + + talloc_free(key_buff); + goto kdf_error; + } + EVP_PKEY_CTX_free(pkey_ctx); + + fr_assert(talloc_array_length(key_buff) == key_len); /* * Ensure the same keys are used across all threads */ if (SSL_CTX_set_tlsext_ticket_keys(ctx, - UNCONST(uint8_t *, cache_conf->session_ticket_key_rand), - sizeof(cache_conf->session_ticket_key_rand)) != 1) { + key_buff, key_len) != 1) { fr_tls_log_strerror_printf(NULL); PERROR("Failed setting session ticket keys"); return -1; } + DEBUG3("Derived session-ticket-key:"); + HEXDUMP3(key_buff, key_len, NULL); + talloc_free(key_buff); + /* * These callbacks embed and extract the * session-state list from the session-ticket. */ - if (SSL_CTX_set_session_ticket_cb(ctx, - tls_cache_session_ticket_app_data_set, - tls_cache_session_ticket_app_data_get, - UNCONST(fr_tls_cache_conf_t *, cache_conf)) != 1) { + if (unlikely(SSL_CTX_set_session_ticket_cb(ctx, + tls_cache_session_ticket_app_data_set, + tls_cache_session_ticket_app_data_get, + UNCONST(fr_tls_cache_conf_t *, cache_conf)) != 1)) { fr_tls_log_strerror_printf(NULL); PERROR("Failed setting session ticket callbacks"); + return -1; } /* @@ -1224,6 +1292,7 @@ int fr_tls_cache_ctx_init(SSL_CTX *ctx, fr_tls_cache_conf_t const *cache_conf) #if OPENSSL_VERSION_NUMBER >= 0x10101000L SSL_CTX_set_num_tickets(ctx, 1); #endif + } break; } diff --git a/src/lib/tls/conf-h b/src/lib/tls/conf-h index 43d1073bf5..dc36c2c4bd 100644 --- a/src/lib/tls/conf-h +++ b/src/lib/tls/conf-h @@ -107,7 +107,8 @@ typedef struct { bool require_pfs; //!< Only allow session resumption if a cipher suite that //!< supports perfect forward secrecy. - uint8_t session_ticket_key_rand[16 + 32 + 32]; //!< OpenSSL really needs to export this length. + uint8_t const *session_ticket_key; //!< Raw input data. Is fed through HKDF to produce the + ///< actual session key we use. } fr_tls_cache_conf_t; /** Certificate verification configuration @@ -117,7 +118,7 @@ typedef struct { fr_tls_verify_mode_t mode; //!< What certificates we apply OpenSSL's pre-validation ///< mode to. - fr_tls_verify_mode_t attribute_mode; //!< What set of certificates we're going to convert to + fr_tls_verify_mode_t attribute_mode; //!< What set of certificates we're going to convert to ///< pairs for verification. bool check_crl; //!< Check certificate revocation lists. diff --git a/src/lib/tls/conf.c b/src/lib/tls/conf.c index dcaf2148ee..4a60f5b0d6 100644 --- a/src/lib/tls/conf.c +++ b/src/lib/tls/conf.c @@ -95,6 +95,8 @@ static CONF_PARSER tls_cache_config[] = { { FR_CONF_OFFSET("require_perfect_forward_secrecy", FR_TYPE_BOOL, fr_tls_cache_conf_t, require_pfs), .dflt = "no" }, #endif + { FR_CONF_OFFSET("session_ticket_key", FR_TYPE_OCTETS, fr_tls_cache_conf_t, session_ticket_key) }, + /* * Deprecated */ @@ -471,7 +473,15 @@ fr_tls_conf_t *fr_tls_conf_parse_server(CONF_SECTION *cs) * Generate random, ephemeral, session-ticket keys. */ if (conf->cache.mode & FR_TLS_CACHE_STATELESS) { - fr_rand_buffer(conf->cache.session_ticket_key_rand, sizeof(conf->cache.session_ticket_key_rand)); + /* + * Fill the key with randomness if one + * wasn't specified by the user. + */ + if (!conf->cache.session_ticket_key) { + MEM(conf->cache.session_ticket_key = talloc_array(conf, uint8_t, 256)); + fr_rand_buffer(UNCONST(uint8_t *, conf->cache.session_ticket_key), + talloc_array_length(conf->cache.session_ticket_key)); + } } /*