diff options
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_eap/libeap/mppe_keys.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/modules/rlm_eap/libeap/mppe_keys.c b/src/modules/rlm_eap/libeap/mppe_keys.c new file mode 100644 index 0000000..385441c --- /dev/null +++ b/src/modules/rlm_eap/libeap/mppe_keys.c @@ -0,0 +1,384 @@ +/* + * mppe_keys.c + * + * Version: $Id$ + * + * 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 + * + * Copyright 2002 Axis Communications AB + * Copyright 2006 The FreeRADIUS server project + * Authors: Henrik Eriksson <henriken@axis.com> & Lars Viklund <larsv@axis.com> + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include "eap_tls.h" +#include <openssl/ssl.h> +#include <openssl/hmac.h> +#include <freeradius-devel/openssl3.h> + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/provider.h> +#endif + +/* + * TLS P_hash from RFC 2246/5246 section 5 + */ +static void P_hash(EVP_MD const *evp_md, + unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + HMAC_CTX *ctx_a, *ctx_out; + unsigned char a[EVP_MAX_MD_SIZE]; + unsigned int size = EVP_MAX_MD_SIZE; + unsigned int digest_len; + + ctx_a = HMAC_CTX_new(); + ctx_out = HMAC_CTX_new(); + HMAC_Init_ex(ctx_a, secret, secret_len, evp_md, NULL); + HMAC_Init_ex(ctx_out, secret, secret_len, evp_md, NULL); + + /* Calculate A(1) */ + HMAC_Update(ctx_a, seed, seed_len); + HMAC_Final(ctx_a, a, &size); + + while (1) { + /* Calculate next part of output */ + HMAC_Update(ctx_out, a, size); + HMAC_Update(ctx_out, seed, seed_len); + + /* Check if last part */ + if (out_len < size) { + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_out, a, &digest_len); + memcpy(out, a, out_len); + break; + } + + /* Place digest in output buffer */ + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_out, out, &digest_len); + HMAC_Init_ex(ctx_out, NULL, 0, NULL, NULL); + out += size; + out_len -= size; + + /* Calculate next A(i) */ + HMAC_Init_ex(ctx_a, NULL, 0, NULL, NULL); + HMAC_Update(ctx_a, a, size); + digest_len = EVP_MAX_MD_SIZE; + HMAC_Final(ctx_a, a, &digest_len); + } + + HMAC_CTX_free(ctx_a); + HMAC_CTX_free(ctx_out); + memset(a, 0, sizeof(a)); +} + +/* + * TLS PRF from RFC 2246 section 5 + */ +static void PRF(unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + uint8_t buf[out_len + (out_len % SHA_DIGEST_LENGTH)]; + unsigned int i; + + unsigned int len = (secret_len + 1) / 2; + uint8_t const *s1 = secret; + uint8_t const *s2 = secret + (secret_len - len); + + EVP_MD const *md5 = NULL; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MD *md5_to_free = NULL; + + /* + * If we are using OpenSSL >= 3.0 and FIPS mode is + * enabled, we need to load the default provider in a + * standalone context in order to access MD5. + */ + OSSL_LIB_CTX *libctx = NULL; + OSSL_PROVIDER *default_provider = NULL; + + if (EVP_default_properties_is_fips_enabled(NULL)) { + libctx = OSSL_LIB_CTX_new(); + default_provider = OSSL_PROVIDER_load(libctx, "default"); + + if (!default_provider) { + ERROR("Failed loading OpenSSL default provider."); + return; + } + + md5_to_free = EVP_MD_fetch(libctx, "MD5", NULL); + if (!md5_to_free) { + ERROR("Failed loading OpenSSL MD5 function."); + return; + } + + md5 = md5_to_free; + } else { + md5 = EVP_md5(); + } +#else + md5 = EVP_md5(); +#endif + + P_hash(md5, s1, len, seed, seed_len, out, out_len); + P_hash(EVP_sha1(), s2, len, seed, seed_len, buf, out_len); + + for (i = 0; i < out_len; i++) { + out[i] ^= buf[i]; + } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + if (libctx) { + OSSL_PROVIDER_unload(default_provider); + OSSL_LIB_CTX_free(libctx); + EVP_MD_free(md5_to_free); + } +#endif +} + +/* + * TLS 1.2 PRF from RFC 5246 section 5 + */ +static void PRFv12(unsigned char const *secret, unsigned int secret_len, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + P_hash(EVP_sha256(), secret, secret_len, seed, seed_len, out, out_len); +} + +/* EAP-FAST Pseudo-Random Function (T-PRF): RFC 4851, Section 5.5 */ +void T_PRF(unsigned char const *secret, unsigned int secret_len, + char const *prf_label, + unsigned char const *seed, unsigned int seed_len, + unsigned char *out, unsigned int out_len) +{ + size_t prf_size = strlen(prf_label); + size_t pos; + uint8_t *buf; + + if (prf_size > 128) prf_size = 128; + prf_size++; /* include trailing zero */ + + buf = talloc_size(NULL, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); + + memcpy(buf + SHA1_DIGEST_LENGTH, prf_label, prf_size); + if (seed) memcpy(buf + SHA1_DIGEST_LENGTH + prf_size, seed, seed_len); + *(uint16_t *)&buf[SHA1_DIGEST_LENGTH + prf_size + seed_len] = htons(out_len); + buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2] = 1; + + // T1 is just the seed + fr_hmac_sha1(buf, buf + SHA1_DIGEST_LENGTH, prf_size + seed_len + 2 + 1, secret, secret_len); + +#define MIN(a,b) (((a)>(b)) ? (b) : (a)) + memcpy(out, buf, MIN(out_len, SHA1_DIGEST_LENGTH)); + + pos = SHA1_DIGEST_LENGTH; + while (pos < out_len) { + buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2]++; + + fr_hmac_sha1(buf, buf, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1, secret, secret_len); + memcpy(&out[pos], buf, MIN(out_len - pos, SHA1_DIGEST_LENGTH)); + + if (out_len - pos <= SHA1_DIGEST_LENGTH) + break; + + pos += SHA1_DIGEST_LENGTH; + } + + memset(buf, 0, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); + talloc_free(buf); +} + +#define EAPTLS_MPPE_KEY_LEN 32 + +/* + * Generate keys according to RFC 2716 and add to reply + */ +void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size) +{ + uint8_t out[4 * EAPTLS_MPPE_KEY_LEN]; + uint8_t *p; + size_t len; + + len = strlen(label); + +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (SSL_export_keying_material(s, out, sizeof(out), label, len, context, context_size, context != NULL) != 1) { + ERROR("Failed generating keying material"); + return; + } +#else + { + uint8_t seed[64 + (2 * SSL3_RANDOM_SIZE) + (context ? 2 + context_size : 0)]; + uint8_t buf[4 * EAPTLS_MPPE_KEY_LEN]; + + p = seed; + + memcpy(p, label, len); + p += len; + + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + len += SSL3_RANDOM_SIZE; + + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + len += SSL3_RANDOM_SIZE; + + if (context) { + /* cloned and reversed FR_PUT_LE16 */ + p[0] = ((uint16_t) (context_size)) >> 8; + p[1] = ((uint16_t) (context_size)) & 0xff; + p += 2; + len += 2; + memcpy(p, context, context_size); + p += context_size; + len += context_size; + } + + PRF(s->session->master_key, s->session->master_key_length, + seed, len, out, buf, sizeof(out)); + } +#endif + + p = out; + eap_add_reply(request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN); + p += EAPTLS_MPPE_KEY_LEN; + eap_add_reply(request, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN); + + eap_add_reply(request, "EAP-MSK", out, 64); + eap_add_reply(request, "EAP-EMSK", out + 64, 64); +} + + +#define FR_TLS_PRF_CHALLENGE "ttls challenge" + +/* + * Generate the TTLS challenge + * + * It's in the TLS module simply because it's only a few lines + * of code, and it needs access to the TLS PRF functions. + */ +void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + if (SSL_export_keying_material(s, buffer, size, FR_TLS_PRF_CHALLENGE, + sizeof(FR_TLS_PRF_CHALLENGE)-1, NULL, 0, 0) != 1) { + ERROR("Failed generating keying material"); + } +#else + uint8_t out[32], buf[32]; + uint8_t seed[sizeof(FR_TLS_PRF_CHALLENGE)-1 + 2*SSL3_RANDOM_SIZE]; + uint8_t *p = seed; + + memcpy(p, FR_TLS_PRF_CHALLENGE, sizeof(FR_TLS_PRF_CHALLENGE)-1); + p += sizeof(FR_TLS_PRF_CHALLENGE)-1; + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + + PRF(s->session->master_key, s->session->master_key_length, + seed, sizeof(seed), out, buf, sizeof(out)); + memcpy(buffer, out, size); +#endif +} + +#define FR_TLS_EXPORTER_METHOD_ID "EXPORTER_EAP_TLS_Method-Id" + +/* + * Actually generates EAP-Session-Id, which is an internal server + * attribute. Not all systems want to send EAP-Key-Name. + */ +void eaptls_gen_eap_key(eap_handler_t *handler) +{ + RADIUS_PACKET *packet = handler->request->reply; + tls_session_t *tls_session = handler->opaque; + SSL *s = tls_session->ssl; + VALUE_PAIR *vp; + uint8_t *buff, *p; + uint8_t type = handler->type & 0xff; + + vp = fr_pair_afrom_num(packet, PW_EAP_SESSION_ID, 0); + if (!vp) return; + + vp->vp_length = 1 + 2 * SSL3_RANDOM_SIZE; + buff = p = talloc_array(vp, uint8_t, vp->vp_length); + + *p++ = type; + + switch (SSL_version(tls_session->ssl)) { + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + SSL_get_client_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + SSL_get_server_random(s, p, SSL3_RANDOM_SIZE); + break; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: +#endif + default: + { + uint8_t const context[] = { type }; + + if (SSL_export_keying_material(s, p, 2 * SSL3_RANDOM_SIZE, + FR_TLS_EXPORTER_METHOD_ID, sizeof(FR_TLS_EXPORTER_METHOD_ID)-1, + context, sizeof(context), 1) != 1) { + ERROR("Failed generating keying material"); + return; + } + } +#endif + } + + vp->vp_octets = buff; + fr_pair_add(&packet->vps, vp); +} + +/* + * Same as before, but for EAP-FAST the order of {server,client}_random is flipped + */ +void eap_fast_tls_gen_challenge(SSL *s, int version, uint8_t *buffer, size_t size, char const *prf_label) +{ + uint8_t *p; + size_t len, master_key_len; + uint8_t seed[128 + 2*SSL3_RANDOM_SIZE]; + uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH]; + + len = strlen(prf_label); + if (len > 128) len = 128; + + p = seed; + memcpy(p, prf_label, len); + p += len; + SSL_get_server_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + SSL_get_client_random(s, p, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + + master_key_len = SSL_SESSION_get_master_key(SSL_get_session(s), master_key, sizeof(master_key)); + + if (version == TLS1_2_VERSION) + PRFv12(master_key, master_key_len, seed, p - seed, buffer, size); + else + PRF(master_key, master_key_len, seed, p - seed, buffer, size); +} |