From 610f9d28f0d7bf1de4d9c585d4b82636fb756194 Mon Sep 17 00:00:00 2001 From: Tim Kourt Date: Wed, 5 Dec 2018 14:34:07 -0800 Subject: [PATCH] eap-ttls: Migrate to eap-tls-common framework The conversion transitions EAP-TTLS implementation to use a common Phase 1 implementation shared among all TLS based EAP methods. --- src/eap-ttls.c | 765 ++++++++++--------------------------------------- 1 file changed, 151 insertions(+), 614 deletions(-) diff --git a/src/eap-ttls.c b/src/eap-ttls.c index fc53adfc..fd5bfd66 100644 --- a/src/eap-ttls.c +++ b/src/eap-ttls.c @@ -29,11 +29,11 @@ #include #include -#include "util.h" -#include "mschaputil.h" -#include "eap.h" -#include "eap-private.h" -#include "eap-tls-common.h" +#include "src/util.h" +#include "src/mschaputil.h" +#include "src/eap.h" +#include "src/eap-private.h" +#include "src/eap-tls-common.h" #define TTLS_AVP_HEADER_LEN 8 #define TTLS_AVP_LEN_MASK 0xFFFFFF @@ -347,8 +347,7 @@ static bool avp_iter_next(struct avp_iter *iter) return true; } -struct phase2_method { - void *state; +struct phase2_method_ops { bool (*init)(struct eap_state *eap); bool (*handle_avp)(struct eap_state *eap, enum radius_attr type, uint32_t vendor_id, const uint8_t *data, @@ -357,95 +356,11 @@ struct phase2_method { bool (*reset)(void *state); }; -struct eap_ttls_state { - char *ca_cert; - char *client_cert; - char *client_key; - char *passphrase; - - struct l_tls *tls; - uint8_t *rx_pkt_buf; - size_t rx_pkt_received, rx_pkt_len; - uint8_t *tx_pkt_buf; - size_t tx_pkt_len, tx_pkt_capacity, tx_pkt_offset; - struct databuf *avp_buf; - bool completed; - uint8_t negotiated_version; - - struct phase2_method *phase2; +struct phase2_method { + void *state; + const struct phase2_method_ops *ops; }; -static void __eap_ttls_reset_state(struct eap_ttls_state *ttls) -{ - ttls->completed = false; - - l_free(ttls->rx_pkt_buf); - ttls->rx_pkt_buf = NULL; - ttls->rx_pkt_received = 0; - ttls->rx_pkt_len = 0; - - l_free(ttls->tx_pkt_buf); - ttls->tx_pkt_buf = NULL; - ttls->tx_pkt_capacity = 0; - ttls->tx_pkt_len = 0; - ttls->tx_pkt_offset = 0; - - databuf_free(ttls->avp_buf); - ttls->avp_buf = NULL; - - if (ttls->tls) { - l_tls_free(ttls->tls); - ttls->tls = NULL; - } -} - -static bool eap_ttls_reset_state(struct eap_state *eap) -{ - struct eap_ttls_state *ttls = eap_get_data(eap); - - if (ttls->phase2->reset) - ttls->phase2->reset(ttls->phase2->state); - - __eap_ttls_reset_state(ttls); - - return true; -} - -static void eap_ttls_free(struct eap_state *eap) -{ - struct eap_ttls_state *ttls = eap_get_data(eap); - - __eap_ttls_reset_state(ttls); - - if (ttls->phase2->destroy) { - ttls->phase2->destroy(ttls->phase2->state); - ttls->phase2->state = NULL; - } - - eap_set_data(eap, NULL); - - l_free(ttls->ca_cert); - l_free(ttls->client_cert); - l_free(ttls->client_key); - - if (ttls->passphrase) { - memset(ttls->passphrase, 0, strlen(ttls->passphrase)); - l_free(ttls->passphrase); - } - - l_free(ttls); -} - -#define EAP_TTLS_RESPONSE_HEADER_LEN 10 - -#define EAP_TTLS_FLAG_L (1 << 7) -#define EAP_TTLS_FLAG_M (1 << 6) -#define EAP_TTLS_FLAG_S (1 << 5) -#define EAP_TTLS_FLAG_MASK \ - (EAP_TTLS_FLAG_L | EAP_TTLS_FLAG_M | EAP_TTLS_FLAG_S) -#define EAP_TTLS_FLAG_LM_MASK \ - (EAP_TTLS_FLAG_L | EAP_TTLS_FLAG_M) - struct phase2_credentials { char *username; char *password; @@ -505,18 +420,18 @@ error: return false; } -static void eap_ttls_phase2_chap_generate_challenge(struct l_tls *tunnel, +static void eap_ttls_phase2_chap_generate_challenge(struct eap_state *eap, uint8_t *challenge, size_t challenge_len) { - l_tls_prf_get_bytes(tunnel, true, - "ttls challenge", challenge, challenge_len); + eap_tls_common_tunnel_prf_get_bytes(eap, true, "ttls challenge", + challenge, challenge_len); } static bool eap_ttls_phase2_chap_init(struct eap_state *eap) { - struct eap_ttls_state *ttls = eap_get_data(eap); - struct phase2_credentials *credentials = ttls->phase2->state; + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); + struct phase2_credentials *credentials = phase2->state; struct avp_builder *builder; uint8_t challenge[CHAP_CHALLENGE_LEN + CHAP_IDENT_LEN]; uint8_t password_hash[CHAP_PASSWORD_LEN]; @@ -525,7 +440,7 @@ static bool eap_ttls_phase2_chap_init(struct eap_state *eap) uint8_t *data; size_t data_len; - eap_ttls_phase2_chap_generate_challenge(ttls->tls, challenge, + eap_ttls_phase2_chap_generate_challenge(eap, challenge, CHAP_CHALLENGE_LEN + CHAP_IDENT_LEN); @@ -553,21 +468,21 @@ static bool eap_ttls_phase2_chap_init(struct eap_state *eap) data = avp_builder_free(builder, false, &data_len); - l_tls_write(ttls->tls, data, data_len); + eap_tls_common_tunnel_send(eap, data, data_len); l_free(data); return true; } -static struct phase2_method phase2_chap = { +static const struct phase2_method_ops phase2_chap_ops = { .init = eap_ttls_phase2_chap_init, .destroy = eap_ttls_phase2_credentials_destroy, }; static bool eap_ttls_phase2_ms_chap_init(struct eap_state *eap) { - struct eap_ttls_state *ttls = eap_get_data(eap); - struct phase2_credentials *credentials = ttls->phase2->state; + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); + struct phase2_credentials *credentials = phase2->state; struct avp_builder *builder; uint8_t challenge[MS_CHAP_CHALLENGE_LEN + CHAP_IDENT_LEN]; uint8_t password_hash[16]; @@ -575,7 +490,7 @@ static bool eap_ttls_phase2_ms_chap_init(struct eap_state *eap) uint8_t *data; size_t data_len; - eap_ttls_phase2_chap_generate_challenge(ttls->tls, challenge, + eap_ttls_phase2_chap_generate_challenge(eap, challenge, MS_CHAP_CHALLENGE_LEN + CHAP_IDENT_LEN); @@ -592,21 +507,21 @@ static bool eap_ttls_phase2_ms_chap_init(struct eap_state *eap) data = avp_builder_free(builder, false, &data_len); - l_tls_write(ttls->tls, data, data_len); + eap_tls_common_tunnel_send(eap, data, data_len); l_free(data); return true; } -static struct phase2_method phase2_ms_chap = { +static const struct phase2_method_ops phase2_mschap_ops = { .init = eap_ttls_phase2_ms_chap_init, .destroy = eap_ttls_phase2_credentials_destroy, }; static bool eap_ttls_phase2_pap_init(struct eap_state *eap) { - struct eap_ttls_state *ttls = eap_get_data(eap); - struct phase2_credentials *state = ttls->phase2->state; + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); + struct phase2_credentials *state = phase2->state; struct avp_builder *builder; uint8_t *buf; size_t buf_len; @@ -618,13 +533,13 @@ static bool eap_ttls_phase2_pap_init(struct eap_state *eap) buf = avp_builder_free(builder, false, &buf_len); - l_tls_write(ttls->tls, buf, buf_len); + eap_tls_common_tunnel_send(eap, buf, buf_len); l_free(buf); return true; } -static struct phase2_method phase2_pap = { +static const struct phase2_method_ops phase2_pap_ops = { .init = eap_ttls_phase2_pap_init, .destroy = eap_ttls_phase2_credentials_destroy, }; @@ -633,7 +548,6 @@ static void eap_ttls_phase2_eap_send_response(const uint8_t *data, size_t len, void *user_data) { struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); struct avp_builder *builder; uint8_t *msg_data; size_t msg_data_len; @@ -646,7 +560,7 @@ static void eap_ttls_phase2_eap_send_response(const uint8_t *data, size_t len, msg_data = avp_builder_free(builder, false, &msg_data_len); - l_tls_write(ttls->tls, msg_data, msg_data_len); + eap_tls_common_tunnel_send(eap, msg_data, msg_data_len); l_free(msg_data); } @@ -654,27 +568,33 @@ static void eap_ttls_phase2_eap_complete(enum eap_result result, void *user_data) { struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); - ttls->completed = true; + eap_tls_common_set_completed(eap); + + if (result != EAP_RESULT_SUCCESS) { + eap_tls_common_set_phase2_failed(eap); + + return; + } + + eap_method_success(eap); } static bool eap_ttls_phase2_eap_load_settings(struct eap_state *eap, + struct phase2_method *phase2, struct l_settings *settings, const char *prefix) { - struct eap_ttls_state *ttls = eap_get_data(eap); - - ttls->phase2->state = eap_new(eap_ttls_phase2_eap_send_response, + phase2->state = eap_new(eap_ttls_phase2_eap_send_response, eap_ttls_phase2_eap_complete, eap); - if (!ttls->phase2->state) { + if (!phase2->state) { l_error("Could not create the TTLS Phase 2 EAP instance"); return false; } - if (!eap_load_settings(ttls->phase2->state, settings, prefix)) { - eap_free(ttls->phase2->state); + if (!eap_load_settings(phase2->state, settings, prefix)) { + eap_free(phase2->state); return false; } @@ -683,16 +603,16 @@ static bool eap_ttls_phase2_eap_load_settings(struct eap_state *eap, static bool eap_ttls_phase2_eap_init(struct eap_state *eap) { - struct eap_ttls_state *ttls = eap_get_data(eap); + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); uint8_t packet[5] = { EAP_CODE_REQUEST, 0, 0, 5, EAP_TYPE_IDENTITY }; - if (!ttls->phase2->state) + if (!phase2->state) return false; /* * Consume a fake Request/Identity packet so that the EAP instance * starts with its Response/Identity right away. */ - eap_rx_packet(ttls->phase2->state, packet, sizeof(packet)); + eap_rx_packet(phase2->state, packet, sizeof(packet)); return true; } @@ -703,12 +623,12 @@ static bool eap_ttls_phase2_eap_handle_avp(struct eap_state *eap, const uint8_t *data, size_t len) { - struct eap_ttls_state *ttls = eap_get_data(eap); + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); if (type != RADIUS_ATTR_EAP_MESSAGE) return false; - eap_rx_packet(ttls->phase2->state, data, len); + eap_rx_packet(phase2->state, data, len); return true; } @@ -730,58 +650,19 @@ static bool eap_ttls_phase2_eap_reset(void *state) return eap_reset(state); } -static struct phase2_method phase2_eap = { +static const struct phase2_method_ops phase2_eap_ops = { .init = eap_ttls_phase2_eap_init, .handle_avp = eap_ttls_phase2_eap_handle_avp, .destroy = eap_ttls_phase2_eap_destroy, .reset = eap_ttls_phase2_eap_reset, }; -static uint8_t *eap_ttls_tx_buf_reserve(struct eap_ttls_state *ttls, - size_t size) +static bool eap_ttls_tunnel_ready(struct eap_state *eap, + const char *peer_identity) { - int offset = ttls->tx_pkt_offset + ttls->tx_pkt_len; - size_t end_offset = offset + size; - - ttls->tx_pkt_len += size; - - if (end_offset > ttls->tx_pkt_capacity) { - ttls->tx_pkt_capacity = end_offset + 1024; - ttls->tx_pkt_buf = - l_realloc(ttls->tx_pkt_buf, ttls->tx_pkt_capacity); - } - - return ttls->tx_pkt_buf + offset; -} - -static void eap_ttls_tx_cb(const uint8_t *data, size_t len, void *user_data) -{ - struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); - - memcpy(eap_ttls_tx_buf_reserve(ttls, len), data, len); -} - -static void eap_ttls_data_cb(const uint8_t *data, size_t data_len, - void *user_data) -{ - struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); - - if (!ttls->avp_buf) - ttls->avp_buf = databuf_new(data_len); - - databuf_append(ttls->avp_buf, data, data_len); -} - -static void eap_ttls_ready_cb(const char *peer_identity, void *user_data) -{ - struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); uint8_t msk_emsk[128]; - /* TODO: if we have a CA certificate require non-NULL peer_identity */ - /* * TTLSv0 seems to assume that the TLS handshake phase authenticates * the server to the client enough that the inner method success or @@ -792,320 +673,71 @@ static void eap_ttls_ready_cb(const char *peer_identity, void *user_data) eap_method_success(eap); /* MSK, EMSK and challenge derivation */ - l_tls_prf_get_bytes(ttls->tls, true, - "ttls keying material", msk_emsk, 128); + eap_tls_common_tunnel_prf_get_bytes(eap, true, "ttls keying material", + msk_emsk, 128); - eap_set_key_material(eap, msk_emsk + 0, 64, msk_emsk + 64, 64, - NULL, 0); + eap_set_key_material(eap, msk_emsk + 0, 64, msk_emsk + 64, 64, NULL, 0); - if (!ttls->phase2->state) - goto err; + if (!phase2->state) + return false; - if (ttls->phase2->init) - ttls->phase2->init(eap); + if (phase2->ops->init) + phase2->ops->init(eap); - return; -err: - l_tls_close(ttls->tls); + return true; } -static void eap_ttls_disconnect_cb(enum l_tls_alert_desc reason, - bool remote, void *user_data) +static bool eap_ttls_tunnel_handle_request(struct eap_state *eap, + const uint8_t *data, + size_t data_len) { - struct eap_state *eap = user_data; - struct eap_ttls_state *ttls = eap_get_data(eap); - - ttls->completed = true; -} - -static void eap_ttls_debug_cb(const char *str, void *user_data) -{ - l_info("EAP-TTLS %s", str); -} - -static void eap_ttls_handle_payload(struct eap_state *eap, - const uint8_t *pkt, - size_t pkt_len) -{ - struct eap_ttls_state *ttls = eap_get_data(eap); + struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap); struct avp_iter iter; - l_tls_handle_rx(ttls->tls, pkt, pkt_len); + if (!phase2->ops->handle_avp) + return true; - if (!ttls->phase2->handle_avp) - return; - - /* Plaintext phase two data is stored into ttls->avp_buf */ - if (!ttls->avp_buf) - return; - - avp_iter_init(&iter, ttls->avp_buf->data, ttls->avp_buf->len); + avp_iter_init(&iter, data, data_len); while (avp_iter_next(&iter)) { - if (ttls->phase2->handle_avp(eap, iter.type, iter.vendor_id, + if (phase2->ops->handle_avp(eap, iter.type, iter.vendor_id, iter.data, iter.len)) continue; if (iter.flags & TTLS_AVP_FLAG_M) - l_tls_close(ttls->tls); + return false; } - databuf_free(ttls->avp_buf); - ttls->avp_buf = NULL; + return true; } -static void eap_ttls_handle_request(struct eap_state *eap, - const uint8_t *pkt, size_t len) +static void eap_ttls_state_reset(void *data) { - uint8_t flags; - uint32_t total_len; - struct eap_ttls_state *ttls = eap_get_data(eap); - size_t fragment_len; - uint8_t *tx_buf; - - if (len < 1) { - l_error("EAP-TTLS request too short"); - goto err; - } - - flags = pkt[0]; - - pkt += 1; - len -= 1; - - if (!(flags & EAP_TTLS_FLAG_S) && - (flags & 7) != ttls->negotiated_version) { - l_error("Non-zero EAP-TTLS version: %i", flags & 7); - goto err; - } - - /* Check if we're expecting a fragment ACK */ - if (ttls->tx_pkt_len) { - if ((flags & EAP_TTLS_FLAG_MASK) || len) { - l_error("EAP-TTLS request is not an ACK"); - goto err; - } - - /* Send next response fragment, prepend the 6-byte header */ - tx_buf = &ttls->tx_pkt_buf[ttls->tx_pkt_offset - 6]; - - fragment_len = eap_get_mtu(eap) - 6; - tx_buf[5] = EAP_TTLS_FLAG_M | - ttls->negotiated_version; /* Flags */ - - if (ttls->tx_pkt_len <= fragment_len) { - fragment_len = ttls->tx_pkt_len; - tx_buf[5] = ttls->negotiated_version; /* Flags */ - } - - eap_send_response(eap, EAP_TYPE_TTLS, - tx_buf, fragment_len + 6); - - ttls->tx_pkt_len -= fragment_len; - ttls->tx_pkt_offset += fragment_len; + struct phase2_method *phase2 = data; + if (!phase2->ops->reset) return; - } - /* Complain if S bit is not correct */ - if (!(flags & EAP_TTLS_FLAG_S) == !ttls->tls) { - l_error("EAP-TTLS request S flag invalid"); - goto err; - } + phase2->ops->reset(phase2->state); +} - /* Method can't be restarted */ - if ((flags & EAP_TTLS_FLAG_S) && ttls->completed) { - l_error("EAP-TTLS start after completed"); - goto err; - } +static void eap_ttls_state_destroy(void *data) +{ + struct phase2_method *phase2 = data; - /* Sanity check that first fragmented request has L flag set */ - if ((flags & EAP_TTLS_FLAG_LM_MASK) == EAP_TTLS_FLAG_M && - !ttls->rx_pkt_buf) { - l_error("EAP-TTLS request 1st fragment with no length"); - goto err; - } + if (phase2->ops->destroy) + phase2->ops->destroy(phase2->state); - if (flags & EAP_TTLS_FLAG_L) { - if (len < 4) { - l_error("EAP-TTLS request with L flag too short"); - goto err; - } - - total_len = l_get_be32(pkt); - pkt += 4; - len -= 4; - - if (flags & EAP_TTLS_FLAG_M) { - if (ttls->rx_pkt_buf) - goto add_to_pkt_buf; - - if (total_len > 512*1024) { - l_error("Maximum message size exceeded"); - goto err; - } - - ttls->rx_pkt_buf = l_malloc(total_len); - ttls->rx_pkt_len = total_len; - ttls->rx_pkt_received = 0; - goto add_to_pkt_buf; - } else if (total_len != len && !ttls->rx_pkt_buf) { - /* - * Sanity check length for unfragmented request - * with L flag set - */ - l_error("EAP-TTLS request Length value invalid"); - goto err; - } - } - - if (ttls->rx_pkt_buf) { -add_to_pkt_buf: - if ( - ((flags & EAP_TTLS_FLAG_M) && - ttls->rx_pkt_received + len >= - ttls->rx_pkt_len) || - (!(flags & EAP_TTLS_FLAG_M) && - ttls->rx_pkt_received + len != - ttls->rx_pkt_len)) { - l_error("EAP-TTLS request fragment length mismatch"); - - l_free(ttls->rx_pkt_buf); - ttls->rx_pkt_buf = NULL; - - goto err; - } - - memcpy(ttls->rx_pkt_buf + ttls->rx_pkt_received, pkt, len); - ttls->rx_pkt_received += len; - } - - if (flags & EAP_TTLS_FLAG_M) { - uint8_t buf[6]; - - /* Send an empty response as ACK */ - buf[5] = 0; - eap_send_response(eap, EAP_TYPE_TTLS, buf, 6); - - return; - } - - if (ttls->rx_pkt_buf) { - pkt = ttls->rx_pkt_buf; - len = ttls->rx_pkt_len; - } - - eap_ttls_tx_buf_reserve(ttls, EAP_TTLS_RESPONSE_HEADER_LEN); - ttls->tx_pkt_offset = ttls->tx_pkt_len; - ttls->tx_pkt_len = 0; - - if (flags & EAP_TTLS_FLAG_S) { - ttls->tls = l_tls_new(false, eap_ttls_data_cb, - eap_ttls_tx_cb, eap_ttls_ready_cb, - eap_ttls_disconnect_cb, eap); - - if (!ttls->tls) { - l_error("Creating a TLS instance failed"); - goto err; - } - - if (getenv("IWD_TLS_DEBUG")) - l_tls_set_debug(ttls->tls, eap_ttls_debug_cb, NULL, NULL); - - if (!l_tls_set_auth_data(ttls->tls, ttls->client_cert, - ttls->client_key, ttls->passphrase) || - (ttls->ca_cert && - !l_tls_set_cacert(ttls->tls, ttls->ca_cert))) { - l_error("Error loading EAP-TTLS keys or certificates"); - goto err; - } - - /* - * RFC5281 section 9.1: "For all packets other than a - * Start packet, the Data field consists of the raw - * TLS message sequence or fragment thereof. For a - * Start packet, the Data field may optionally - * contain an AVP sequence." - * We ignore the unencrypted AVP sequence if there is - * any. - */ - len = 0; - } - - if (len) - eap_ttls_handle_payload(eap, pkt, len); - - if (ttls->rx_pkt_buf) { - l_free(ttls->rx_pkt_buf); - ttls->rx_pkt_buf = NULL; - } - - /* - * Note if ttls->completed && !eap->method_success we can send an empty - * response instead of passing the TLS alert. - */ - - if (ttls->tx_pkt_len + 6 <= eap_get_mtu(eap)) { - /* - * Response fits in a single response packet, prepend the - * 6-byte header (no length) before the data. - */ - tx_buf = &ttls->tx_pkt_buf[ttls->tx_pkt_offset - 6]; - - tx_buf[5] = ttls->negotiated_version; /* Flags */ - - eap_send_response(eap, EAP_TYPE_TTLS, - tx_buf, ttls->tx_pkt_len + 6); - - ttls->tx_pkt_len = 0; - } else { - /* - * Fragmentation needed, prepend the 10-byte header - * (4 EAP header + 2 response + 4 length) to build the - * initial fragment packet. - */ - tx_buf = &ttls->tx_pkt_buf[ttls->tx_pkt_offset - 10]; - - tx_buf[5] = EAP_TTLS_FLAG_L | EAP_TTLS_FLAG_M | - ttls->negotiated_version; /* Flags */ - l_put_be32(ttls->tx_pkt_len, &tx_buf[6]); - - fragment_len = eap_get_mtu(eap) - 10; - eap_send_response(eap, EAP_TYPE_TTLS, - tx_buf, fragment_len + 10); - - ttls->tx_pkt_len -= fragment_len; - ttls->tx_pkt_offset += fragment_len; - } - - if (ttls->completed) { - l_tls_free(ttls->tls); - ttls->tls = NULL; - - if (ttls->phase2->destroy) { - ttls->phase2->destroy(ttls->phase2->state); - ttls->phase2->state = NULL; - } - } - - return; - -err: - ttls->completed = true; - - l_tls_free(ttls->tls); - ttls->tls = NULL; - - eap_method_error(eap); + l_free(phase2); } static const struct { const char *name; - struct phase2_method *method; -} tunneled_non_eap_methods[] = { - { "Tunneled-CHAP", &phase2_chap }, - { "Tunneled-MSCHAP", &phase2_ms_chap }, - { "Tunneled-PAP", &phase2_pap }, + const struct phase2_method_ops *method_ops; +} tunneled_non_eap_method_ops[] = { + { "Tunneled-CHAP", &phase2_chap_ops }, + { "Tunneled-MSCHAP", &phase2_mschap_ops }, + { "Tunneled-PAP", &phase2_pap_ops }, { } }; @@ -1155,196 +787,99 @@ static int eap_ttls_check_tunneled_auth_settings(struct l_settings *settings, return 0; } -static int eap_ttls_check_settings(struct l_settings *settings, - struct l_queue *secrets, - const char *prefix, - struct l_queue **out_missing) +static int eap_ttls_settings_check(struct l_settings *settings, + struct l_queue *secrets, + const char *prefix, + struct l_queue **out_missing) { - char setting[64], client_cert_setting[64], passphrase_setting[64]; - L_AUTO_FREE_VAR(char *, path) = NULL; - L_AUTO_FREE_VAR(char *, client_cert) = NULL; - L_AUTO_FREE_VAR(char *, passphrase) = NULL; - uint8_t *cert; - size_t size; - const char *phase2_method; + char setting_key[72]; + char setting_prefix[72]; + const char *phase2_method_name; uint8_t i; + int r; - snprintf(setting, sizeof(setting), "%sTTLS-CACert", prefix); - path = l_settings_get_string(settings, "Security", setting); - if (path) { - cert = l_pem_load_certificate(path, &size); - if (!cert) { - l_error("Failed to load %s", path); - return -EIO; - } + snprintf(setting_prefix, sizeof(setting_prefix), "%sTTLS-", prefix); + r = eap_tls_common_settings_check(settings, secrets, setting_prefix, + out_missing); + if (r) + return r; - l_free(cert); - } + snprintf(setting_key, sizeof(setting_key), "%sTTLS-Phase2-Method", + prefix); + phase2_method_name = l_settings_get_value(settings, "Security", + setting_key); - snprintf(client_cert_setting, sizeof(client_cert_setting), - "%sTTLS-ClientCert", prefix); - client_cert = l_settings_get_string(settings, "Security", - client_cert_setting); - if (client_cert) { - cert = l_pem_load_certificate(client_cert, &size); - if (!cert) { - l_error("Failed to load %s", client_cert); - return -EIO; - } + snprintf(setting_prefix, sizeof(setting_prefix), "%sTTLS-Phase2-", + prefix); - l_free(cert); - } - - l_free(path); - - snprintf(setting, sizeof(setting), "%sTTLS-ClientKey", prefix); - path = l_settings_get_string(settings, "Security", setting); - - if (path && !client_cert) { - l_error("%s present but no client certificate (%s)", - setting, client_cert_setting); - return -ENOENT; - } - - snprintf(passphrase_setting, sizeof(passphrase_setting), - "%sTTLS-ClientKeyPassphrase", prefix); - passphrase = l_settings_get_string(settings, "Security", - passphrase_setting); - - if (!passphrase) { - const struct eap_secret_info *secret; - - secret = l_queue_find(secrets, eap_secret_info_match, - passphrase_setting); - if (secret) - passphrase = l_strdup(secret->value); - } - - if (path) { - struct l_key *priv_key; - bool encrypted; - - priv_key = l_pem_load_private_key(path, passphrase, &encrypted); - if (!priv_key) { - if (!encrypted) { - l_error("Error loading client private key %s", - path); - return -EIO; - } - - if (passphrase) { - l_error("Error loading encrypted client " - "private key %s", path); - return -EACCES; - } - - /* - * We've got an encrypted key and passphrase was not - * saved in the network settings, need to request - * the passphrase. - */ - eap_append_secret(out_missing, - EAP_SECRET_LOCAL_PKEY_PASSPHRASE, - passphrase_setting, NULL, path, - EAP_CACHE_TEMPORARY); - } else { - l_key_free(priv_key); - - if (passphrase && !encrypted) { - l_error("%s present but client private " - "key %s is not encrypted", - passphrase_setting, path); - return -EIO; - } - } - } else if (passphrase) { - l_error("%s present but no client private key path set (%s)", - passphrase_setting, setting); - return -ENOENT; - } - - snprintf(setting, sizeof(setting), "%sTTLS-Phase2-Method", prefix); - phase2_method = l_settings_get_value(settings, "Security", setting); - - snprintf(setting, sizeof(setting), "%sTTLS-Phase2-", prefix); - - for (i = 0; tunneled_non_eap_methods[i].name; i++) { - if (strcmp(tunneled_non_eap_methods[i].name, phase2_method)) + for (i = 0; tunneled_non_eap_method_ops[i].name; i++) { + if (strcmp(tunneled_non_eap_method_ops[i].name, + phase2_method_name)) continue; return eap_ttls_check_tunneled_auth_settings(settings, secrets, - setting, + setting_prefix, out_missing); } - return __eap_check_settings(settings, secrets, setting, false, - out_missing); + return __eap_check_settings(settings, secrets, setting_prefix, false, + out_missing); } -static bool eap_ttls_load_settings(struct eap_state *eap, - struct l_settings *settings, - const char *prefix) +static const struct eap_tls_variant_ops eap_ttls_ops = { + .version_max_supported = EAP_TLS_VERSION_0, + .tunnel_ready = eap_ttls_tunnel_ready, + .tunnel_handle_request = eap_ttls_tunnel_handle_request, + .reset = eap_ttls_state_reset, + .destroy = eap_ttls_state_destroy, +}; + +static bool eap_ttls_settings_load(struct eap_state *eap, + struct l_settings *settings, + const char *prefix) { - struct eap_ttls_state *ttls; - const char *phase2_method; - char setting[64]; + struct phase2_method *phase2 = l_new(struct phase2_method, 1); + const char *phase2_method_name; + char setting[72]; uint8_t i; - ttls = l_new(struct eap_ttls_state, 1); - - snprintf(setting, sizeof(setting), "%sTTLS-CACert", prefix); - ttls->ca_cert = l_settings_get_string(settings, "Security", setting); - - snprintf(setting, sizeof(setting), "%sTTLS-ClientCert", prefix); - ttls->client_cert = l_settings_get_string(settings, - "Security", setting); - - snprintf(setting, sizeof(setting), "%sTTLS-ClientKey", prefix); - ttls->client_key = l_settings_get_string(settings, "Security", setting); - - snprintf(setting, sizeof(setting), "%sTTLS-ClientKeyPassphrase", - prefix); - ttls->passphrase = l_settings_get_string(settings, "Security", setting); - snprintf(setting, sizeof(setting), "%sTTLS-Phase2-Method", prefix); - phase2_method = l_settings_get_value(settings, "Security", setting); + phase2_method_name = l_settings_get_value(settings, "Security", + setting); snprintf(setting, sizeof(setting), "%sTTLS-Phase2-", prefix); - eap_set_data(eap, ttls); - - for (i = 0; tunneled_non_eap_methods[i].name; i++) { - if (strcmp(tunneled_non_eap_methods[i].name, phase2_method)) + for (i = 0; tunneled_non_eap_method_ops[i].name; i++) { + if (strcmp(tunneled_non_eap_method_ops[i].name, + phase2_method_name)) continue; - ttls->phase2 = tunneled_non_eap_methods[i].method; + phase2->ops = tunneled_non_eap_method_ops[i].method_ops; - if (!eap_ttls_phase2_non_eap_load_settings(ttls->phase2, - settings, + if (!eap_ttls_phase2_non_eap_load_settings(phase2, settings, setting)) - goto err; + goto error; break; } - if (!ttls->phase2) { - ttls->phase2 = &phase2_eap; + if (!phase2->ops) { + phase2->ops = &phase2_eap_ops; - if (!eap_ttls_phase2_eap_load_settings(eap, settings, setting)) - goto err; + if (!eap_ttls_phase2_eap_load_settings(eap, phase2, settings, + setting)) + goto error; } - return true; + snprintf(setting, sizeof(setting), "%sTTLS-", prefix); -err: - eap_set_data(eap, NULL); - l_free(ttls->ca_cert); - l_free(ttls->client_cert); - l_free(ttls->client_key); - if (ttls->passphrase) - memset(ttls->passphrase, 0, strlen(ttls->passphrase)); - l_free(ttls->passphrase); - l_free(ttls); + if (!eap_tls_common_settings_load(eap, settings, setting, + &eap_ttls_ops, phase2)) + goto error; + + return true; +error: + l_free(phase2); return false; } @@ -1354,11 +889,13 @@ static struct eap_method eap_ttls = { .exports_msk = true, .name = "TTLS", - .free = eap_ttls_free, - .handle_request = eap_ttls_handle_request, - .check_settings = eap_ttls_check_settings, - .load_settings = eap_ttls_load_settings, - .reset_state = eap_ttls_reset_state, + .handle_request = eap_tls_common_handle_request, + .handle_retransmit = eap_tls_common_handle_retransmit, + .reset_state = eap_tls_common_state_reset, + .free = eap_tls_common_state_free, + + .check_settings = eap_ttls_settings_check, + .load_settings = eap_ttls_settings_load, }; static int eap_ttls_init(void)