#
# Allowed values are "1.0", "1.1", "1.2", and "1.3".
#
- # Note that the server WILL NOT permit negotiation of
- # TLS 1.3. The EAP-TLS standards for TLS 1.3 are NOT
- # finished. It is therefore impossible for the server
- # to negotiate EAP-TLS correctly with TLS 1.3.
- #
# The values must be in quotes.
#
tls_min_version = "1.2"
void *opaque;
void (*free_opaque)(void *opaque);
- char const *prf_label;
+ char const *label;
bool allow_session_resumption; //!< Whether session resumption is allowed.
} tls_session_t;
state->info.alert_level = 0x00;
state->info.alert_description = 0x00;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ } else if (content_type == SSL3_RT_INNER_CONTENT_TYPE && buf[0] == SSL3_RT_APPLICATION_DATA) {
+ /* let tls_ack_handler set application_data */
+ state->info.content_type = SSL3_RT_HANDSHAKE;
+#endif
+
#ifdef SSL3_RT_HEARTBEAT
} else if (content_type == TLS1_RT_HEARTBEAT) {
uint8_t *p = buf;
record_minus(&ssn->clean_in, NULL, written);
/* Get the dirty data from Bio to send it */
- err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,
- sizeof(ssn->dirty_out.data));
+ err = BIO_read(ssn->from_ssl, ssn->dirty_out.data + ssn->dirty_out.used,
+ sizeof(ssn->dirty_out.data) - ssn->dirty_out.used);
if (err > 0) {
- ssn->dirty_out.used = err;
+ ssn->dirty_out.used += err;
} else {
if (!tls_error_io_log(request, ssn, err,
"Failed in " STRINGIFY(__FUNCTION__) " (SSL_write)")) {
ctx_options |= SSL_OP_NO_SSLv2;
ctx_options |= SSL_OP_NO_SSLv3;
+ /*
+ * If set then dummy Change Cipher Spec (CCS) messages are sent in
+ * TLSv1.3. This has the effect of making TLSv1.3 look more like TLSv1.2
+ * so that middleboxes that do not understand TLSv1.3 will not drop
+ * the connection. This isn't needed for EAP-TLS, so we disable it.
+ *
+ * EAP (hopefully) does not have middlebox deployments
+ */
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+ ctx_options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT;
+#endif
+
/*
* SSL_CTX_set_(min|max)_proto_version was included in OpenSSL 1.1.0
*
* Pick the maximum one we know about.
*/
#ifdef TLS1_4_VERSION
- max_version = TLS1_2_VERSION; /* NOT a typo! EAP methods for TLS 1.4 are NOT finished */
+ max_version = TLS1_3_VERSION; /* NOT a typo! EAP methods for TLS 1.4 are NOT finished */
#elif defined(TLS1_3_VERSION)
- max_version = TLS1_2_VERSION; /* NOT a typo! EAP methods for TLS 1.3 are NOT finished */
+ max_version = TLS1_3_VERSION;
#elif defined(TLS1_2_VERSION)
max_version = TLS1_2_VERSION;
#elif defined(TLS1_1_VERSION)
SSL_CTX_set_options(ctx, ctx_options);
+ /*
+ * TLS 1.3 introduces the concept of early data (also known as zero
+ * round trip data or 0-RTT data). Early data allows a client to send
+ * data to a server in the first round trip of a connection, without
+ * waiting for the TLS handshake to complete if the client has spoken
+ * to the same server recently. This doesn't work for EAP, so we
+ * disable early data.
+ *
+ * OpenSSL 1.1.1 and later set this as the default
+ */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER < 0x10101000L
+ SSL_CTX_set_max_early_data(ctx, 0);
+#endif
+
/*
* TODO: Set the RSA & DH
* SSL_CTX_set_tmp_rsa_callback(ctx, cbtls_rsa);
/*
* Automatically generate MPPE keying material.
*/
- if (tls_session->prf_label) {
+ if (tls_session->label) {
+ uint8_t const *context = NULL;
+ size_t context_size = 0;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ uint8_t const context_tls13[] = { handler->type };
+#endif
+
+ switch (tls_session->info.version) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ case TLS1_3_VERSION:
+ context = context_tls13;
+ context_size = sizeof(context_tls13);
+ break;
+#endif
+ case TLS1_2_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_VERSION:
+ break;
+ case SSL2_VERSION:
+ case SSL3_VERSION:
+ default:
+ /* Should never happen */
+ rad_assert(0);
+ return 0;
+ break;
+ }
eaptls_gen_mppe_keys(handler->request,
- tls_session->ssl, tls_session->prf_label);
+ tls_session->ssl, tls_session->label,
+ context, context_size);
} else if (handler->type != PW_EAP_FAST) {
RWDEBUG("Not adding MPPE keys because there is no PRF label");
}
- eaptls_gen_eap_key(handler->request->reply, tls_session->ssl,
- handler->type);
+ eaptls_gen_eap_key(handler->request->reply, tls_session->ssl, handler->type);
+
return 1;
}
return FR_TLS_FAIL;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ /* draft-ietf-emu-eap-tls13-10 section 2.5 */
+ if (tls_session->is_init_finished && tls_session->info.version == TLS1_3_VERSION && handler->type == PW_EAP_TLS) {
+ RDEBUG("TLS send Commitment Message");
+ tls_session->record_plus(&tls_session->clean_in, "\0", 1);
+ tls_handshake_send(request, tls_session);
+ }
+#endif
+
/*
* FIXME: return success/fail.
*
/*
* If there is no data to send i.e
* dirty_out.used <=0 and if the SSL
- * handshake is finished, then return a
- * EPTLS_SUCCESS
+ * handshake is finished.
*/
if (tls_session->is_init_finished) {
int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) CC_HINT(nonnull);
-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) CC_HINT(nonnull(1,3,6));
-void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label);
+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) CC_HINT(nonnull(1,3,6));
+void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, size_t context_size);
void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size);
void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header);
-void eap_fast_tls_gen_challenge(SSL *ssl, uint8_t *buffer, uint8_t *scratch, size_t size, char const *prf_label) CC_HINT(nonnull);
+void eap_fast_tls_gen_challenge(SSL *ssl, uint8_t *buffer, uint8_t *scratch, size_t size, char const *prf_label) CC_HINT(nonnull);
#define BUFFER_SIZE 1024
/*
* Generate keys according to RFC 2716 and add to reply
*/
-void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label)
+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 prf_size;
+ size_t len;
- prf_size = strlen(prf_label);
+ len = strlen(label);
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- if (SSL_export_keying_material(s, out, sizeof(out), prf_label, prf_size, NULL, 0, 0) != 1) {
+ 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)];
+ 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, prf_label, prf_size);
- p += prf_size;
+ memcpy(p, label, len);
+ p += len;
memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE);
p += SSL3_RANDOM_SIZE;
- prf_size += SSL3_RANDOM_SIZE;
+ len += SSL3_RANDOM_SIZE;
memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE);
- prf_size += 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, prf_size, out, buf, sizeof(out));
+ seed, len, out, buf, sizeof(out));
}
#endif
}
-#define FR_TLS_PRF_CHALLENGE "ttls challenge"
+#define FR_TLS_PRF_CHALLENGE "ttls challenge"
/*
* Generate the TTLS challenge
void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size)
{
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- SSL_export_keying_material(s, buffer, size, FR_TLS_PRF_CHALLENGE,
- sizeof(FR_TLS_PRF_CHALLENGE) - 1, NULL, 0, 0);
-
+ 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];
#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(RADIUS_PACKET *packet, SSL *ssl, uint32_t header)
+void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header)
{
VALUE_PAIR *vp;
uint8_t *buff, *p;
*p++ = header & 0xff;
- SSL_get_client_random(ssl, p, SSL3_RANDOM_SIZE);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ {
+ uint8_t const context[] = { header };
+
+ if (SSL_export_keying_material(s, p, 2 * SSL3_RANDOM_SIZE,
+ FR_TLS_EXPORTER_METHOD_ID, sizeof(FR_TLS_EXPORTER_METHOD_ID)-1,
+ context, 1, 1) != 1) {
+ ERROR("Failed generating keying material");
+ return;
+ }
+ }
+#else
+ SSL_get_client_random(s, p, SSL3_RANDOM_SIZE);
p += SSL3_RANDOM_SIZE;
- SSL_get_server_random(ssl, p, SSL3_RANDOM_SIZE);
+ SSL_get_server_random(s, p, SSL3_RANDOM_SIZE);
+#endif
vp->vp_octets = buff;
fr_pair_add(&packet->vps, vp);
handler->opaque = ((void *)ssn);
- /*
- * Set up type-specific information.
- */
- ssn->prf_label = "client EAP encryption";
-
/*
* As it is a poorly designed protocol, PEAP uses
* bits in the TLS header to indicate PEAP
RDEBUG2("No saved attributes in the original Access-Accept");
}
+ /*
+ * Set the label based on the TLS version negotiated in the handshake.
+ */
+ switch (tls_session->info.version) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ case TLS1_3_VERSION:
+ tls_session->label = "EXPORTER_EAP_TLS_Key_Material";
+ break;
+#endif
+ case TLS1_2_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_VERSION:
+ tls_session->label = "client EAP encryption";
+ break;
+ default:
+ break;
+ }
+
+
/*
* Success: Automatically return MPPE keys.
*/
handler->opaque = ((void *)ssn);
- /*
- * Set up type-specific information.
- */
- ssn->prf_label = "client EAP encryption";
-
/*
* TLS session initialization is over. Now handle TLS
* related handshaking or application data.
/* success */
}
+ /*
+ * Set the label based on the TLS version negotiated in the handshake.
+ */
+ switch (tls_session->info.version) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ case TLS1_3_VERSION:
+ tls_session->label = "EXPORTER_EAP_TLS_Key_Material";
+ break;
+#endif
+ case TLS1_2_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_VERSION:
+ tls_session->label = "client EAP encryption";
+ break;
+ default:
+ break;
+ }
+
/*
* Success: Automatically return MPPE keys.
*/
handler->opaque = ((void *)ssn);
- /*
- * Set up type-specific information.
- */
- ssn->prf_label = "ttls keying material";
-
/*
* TLS session initialization is over. Now handle TLS
* related handshaking or application data.
}
do_keys:
+ /*
+ * Set the label based on the TLS version negotiated in the handshake.
+ */
+ switch (tls_session->info.version) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ case TLS1_3_VERSION:
+ tls_session->label = "EXPORTER_EAP_TLS_Key_Material";
+ break;
+#endif
+ case TLS1_2_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_VERSION:
+ tls_session->label = "ttls keying material";
+ break;
+ default:
+ break;
+ }
+
/*
* Success: Automatically return MPPE keys.
*/
* Success: Automatically return MPPE keys.
*/
case PW_CODE_ACCESS_ACCEPT:
- ret = eaptls_success(handler, 0);
- goto done;
+ goto do_keys;
/*
* No response packet, MUST be proxying it.
*/
if (((vp->da->vendor == 0) && (vp->da->attr == PW_CHAP_CHALLENGE)) ||
((vp->da->vendor == VENDORPEC_MICROSOFT) && (vp->da->attr == PW_MSCHAP_CHALLENGE))) {
- uint8_t challenge[16];
+ uint8_t challenge[17];
if ((vp->vp_length < 8) ||
(vp->vp_length > 16)) {
return NULL;
}
- eapttls_gen_challenge(ssl, challenge,
- sizeof(challenge));
+ /*
+ * TLSv1.3 exports a different key depending on the length
+ * requested so ask for *exactly* what the spec requires
+ */
+ eapttls_gen_challenge(ssl, challenge, vp->vp_length + 1);
if (memcmp(challenge, vp->vp_octets,
vp->vp_length) != 0) {