]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v3.0.x: TLSv1.3 support for EAP-{TLS,TTLS,PEAP} (#3516)
authorAlexander Clouter <aclouter@networkradius.com>
Tue, 7 Jul 2020 11:13:23 +0000 (12:13 +0100)
committerGitHub <noreply@github.com>
Tue, 7 Jul 2020 11:13:23 +0000 (07:13 -0400)
* eap-tls: tls version 1.3

draft-ietf-emu-eap-tls13 (tls) and draft-ietf-emu-tls-eap-types (ttls/peap)

* SQUASH: remove unneeded line, compilers complain about more readable code rather than sensibly optimise away

https://github.com/FreeRADIUS/freeradius-server/pull/3516#pullrequestreview-443451513

* SQUASH: comment about early data

* SQUASH: middleware boxen comment

* SQUASH: method of using context is too confusing retain original behaviour

raddb/mods-available/eap
src/include/tls-h
src/main/cb.c
src/main/tls.c
src/modules/rlm_eap/libeap/eap_tls.c
src/modules/rlm_eap/libeap/eap_tls.h
src/modules/rlm_eap/libeap/mppe_keys.c
src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c
src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c
src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c

index a89a783663588017b12bcc076362e728261ba8f2..7e06a8465409ae661d2a129a354d2b151096321e 100644 (file)
@@ -405,11 +405,6 @@ eap {
                #
                #  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"
index 62f57c4715a611284042edb28c284a7b91a6538b..93a6fbb7e4d76ef91ef0eefb726d3d9ab13a49ee 100644 (file)
@@ -160,7 +160,7 @@ typedef struct _tls_session_t {
        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;
 
index 4ae14e575bef9fa34bb6170b20624320f12abe13..dc708472865a67ca80754dd02bba11332ff22223 100644 (file)
@@ -124,6 +124,12 @@ void cbtls_msg(int write_p, int msg_version, int content_type,
                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;
index 2db998b2abbd6a3c9a70b97b6f31591643db7206..6e483fb0f8627a1e7716f5d12f7fe36a2d4fe0c8 100644 (file)
@@ -858,10 +858,10 @@ int tls_handshake_send(REQUEST *request, tls_session_t *ssn)
                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)")) {
@@ -3091,6 +3091,18 @@ post_ca:
        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
         *
@@ -3119,9 +3131,9 @@ post_ca:
                         *      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)
@@ -3294,6 +3306,20 @@ post_ca:
 
        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);
index bc4bb0c9aba1559dda9cd3bd99e76f11a46f38dc..9b73c482f8363d4a7d5dbdbb0bdee736c36baa4b 100644 (file)
@@ -160,15 +160,41 @@ int eaptls_success(eap_handler_t *handler, int peap_flag)
        /*
         *      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;
 }
 
@@ -724,6 +750,15 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *h
                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.
         *
@@ -737,8 +772,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *h
        /*
         *      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) {
index 73c7fdd53b5a80ea928ea9f007437bee56b8fa51..e82b3711c8947f0b4a07c019048d89d9df1961f3 100644 (file)
@@ -62,11 +62,11 @@ int eaptls_fail(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull);
 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
 
index 3a9e8641048317e57991bccbf33940428ce411bc..78b4ae9f005125d3dc7ecd58d29fd4388c8af09e 100644 (file)
@@ -150,38 +150,50 @@ static void PRF(unsigned char const *secret, unsigned int secret_len,
 /*
  *     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
 
@@ -195,7 +207,7 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label)
 }
 
 
-#define FR_TLS_PRF_CHALLENGE   "ttls challenge"
+#define FR_TLS_PRF_CHALLENGE           "ttls challenge"
 
 /*
  *     Generate the TTLS challenge
@@ -206,9 +218,10 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *prf_label)
 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];
@@ -226,11 +239,13 @@ void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t 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;
@@ -243,9 +258,22 @@ void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *ssl, uint32_t header)
 
        *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);
index 3d23322663cecb7cd65befd31c313a30e7eeeeb5..d3be348ce3baa3b60ef64ef637258c94977a8855 100644 (file)
@@ -199,11 +199,6 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler)
 
        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
@@ -373,6 +368,25 @@ static int mod_process(void *arg, eap_handler_t *handler)
                        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.
                 */
index 4d41cd42e6e5e353db9fc591e3c430ea8ac1c946..305ba21cfcd3dae3720d0edc92c7e26e8b68a681 100644 (file)
@@ -99,11 +99,6 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler)
 
        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.
@@ -195,6 +190,24 @@ static int CC_HINT(nonnull) mod_process(void *type_arg, eap_handler_t *handler)
                        /* 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.
                 */
index a3c575bceb55f4ec787822ee4a9ae0d8570ad356..e9b96b1c8104de35cc8b58091c951c194ae3d9c9 100644 (file)
@@ -188,11 +188,6 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler)
 
        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.
@@ -271,6 +266,24 @@ static int mod_process(void *arg, eap_handler_t *handler)
                        }
 
                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.
                         */
@@ -342,8 +355,7 @@ static int mod_process(void *arg, eap_handler_t *handler)
                 *      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.
index 627d722ed7db10e705253f8ed213af35eb48141f..40d398a9be8b10f945b31ab16c41652db955f731 100644 (file)
@@ -405,7 +405,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                 */
                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)) {
@@ -415,8 +415,11 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                                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) {