| /* |
| * |
| * Wireless daemon for Linux |
| * |
| * Copyright (C) 2013-2019 Intel Corporation. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <ell/ell.h> |
| |
| #include "src/missing.h" |
| #include "src/module.h" |
| #include "src/util.h" |
| #include "src/eap.h" |
| #include "src/eap-private.h" |
| #include "src/iwd.h" |
| |
| static uint32_t default_mtu; |
| static struct l_queue *eap_methods; |
| |
| static void dump_eap(const char *str, void *user_data) |
| { |
| const char *prefix = user_data; |
| |
| l_info("%s%s\n", prefix, str); |
| } |
| |
| struct eap_state { |
| eap_tx_packet_func_t tx_packet; |
| eap_key_material_func_t set_key_material; |
| eap_complete_func_t complete; |
| eap_event_func_t event_func; |
| void *user_data; |
| size_t mtu; |
| |
| struct eap_method *method; |
| char *identity; |
| char *identity_setting; |
| bool authenticator; |
| |
| int last_id; |
| void *method_state; |
| bool method_success; |
| struct l_timeout *complete_timeout; |
| unsigned int identity_req_count; |
| unsigned int method_req_count; |
| |
| bool discard_success_and_failure:1; |
| }; |
| |
| struct eap_state *eap_new(eap_tx_packet_func_t tx_packet, |
| eap_complete_func_t complete, void *user_data) |
| { |
| struct eap_state *eap; |
| |
| eap = l_new(struct eap_state, 1); |
| |
| eap->last_id = -1; |
| eap->mtu = default_mtu; |
| |
| eap->tx_packet = tx_packet; |
| eap->complete = complete; |
| eap->user_data = user_data; |
| |
| return eap; |
| } |
| |
| /* |
| * Setting a non-NULL set_key_material callback for this EAP instance will |
| * disable the legacy methods that don't generate key material, such |
| * as EAP-MD5. |
| */ |
| void eap_set_key_material_func(struct eap_state *eap, |
| eap_key_material_func_t func) |
| { |
| eap->set_key_material = func; |
| } |
| |
| void eap_set_event_func(struct eap_state *eap, eap_event_func_t func) |
| { |
| eap->event_func = func; |
| } |
| |
| bool eap_reset(struct eap_state *eap) |
| { |
| if (eap->method_state && eap->method->reset_state) { |
| if (!eap->method->reset_state(eap)) |
| return false; |
| } |
| |
| eap->method_success = false; |
| l_timeout_remove(eap->complete_timeout); |
| eap->complete_timeout = NULL; |
| |
| return true; |
| } |
| |
| static void eap_free_common(struct eap_state *eap) |
| { |
| if (eap->method_state && eap->method->free) |
| eap->method->free(eap); |
| |
| eap->method = NULL; |
| |
| if (eap->identity) { |
| l_free(eap->identity); |
| eap->identity = NULL; |
| l_free(eap->identity_setting); |
| eap->identity_setting = NULL; |
| } |
| } |
| |
| void eap_free(struct eap_state *eap) |
| { |
| /* |
| * In supplicant mode we get here whether the authenticator has |
| * sent an EAP-Failure or interrupted the connection at a higher |
| * layer, or even when the negotiation stalled and we interrupted |
| * it, so use this code path to check if it is the Identity value |
| * that the authenticator may have a problem with. We don't know |
| * whether we're the phase 1 or phase 2 EAP state machine |
| * (eap->set_key_material is a hint but doesn't work with EAP-PEAP |
| * phase2) so print the setting name. |
| */ |
| if (!eap->authenticator && !eap->method_success) { |
| if (eap->identity_req_count && !eap->method_req_count) |
| l_info("EAP negotiation stopped after the Identity " |
| "exchange, this can happen when the %s value " |
| "is not what the authenticator expects", |
| eap->identity_setting ?: "identity"); |
| } |
| |
| eap_free_common(eap); |
| l_timeout_remove(eap->complete_timeout); |
| |
| l_free(eap); |
| } |
| |
| /* Note: callers must check for a minimum value */ |
| void eap_set_mtu(struct eap_state *eap, size_t mtu) |
| { |
| eap->mtu = mtu; |
| } |
| |
| size_t eap_get_mtu(struct eap_state *eap) |
| { |
| return eap->mtu; |
| } |
| |
| const char *eap_get_identity(struct eap_state *eap) |
| { |
| return eap->identity; |
| } |
| |
| static void eap_send_packet(struct eap_state *eap, enum eap_code code, |
| uint8_t id, uint8_t *buf, size_t len) |
| { |
| buf[0] = code; |
| buf[1] = id; |
| l_put_be16(len, &buf[2]); |
| |
| eap->tx_packet(buf, len, eap->user_data); |
| } |
| |
| static void eap_send_response(struct eap_state *eap, enum eap_type type, |
| uint8_t *buf, size_t len) |
| { |
| buf[4] = type; |
| |
| if (type == EAP_TYPE_EXPANDED) { |
| memcpy(buf + 5, eap->method->vendor_id, 3); |
| l_put_be32(eap->method->vendor_type, buf + 8); |
| } |
| |
| eap_send_packet(eap, EAP_CODE_RESPONSE, eap->last_id, buf, len); |
| } |
| |
| /** |
| * eap_method_respond: |
| * @eap: EAP state |
| * @buf: Buffer to send |
| * @len: Size of the buffer |
| * |
| * Sends out a response to a received request. This method first fills |
| * the EAP header in the buffer based on the method's EAP type being |
| * sent. |
| * |
| * If the method uses an expanded type , then the Vendor-Id and |
| * Vendor-Type fields are filled in automatically. |
| * |
| * The buffer passed in MUST be at least 12 bytes long if method uses |
| * an expanded type and at least 5 bytes for other cases. |
| **/ |
| void eap_method_respond(struct eap_state *eap, uint8_t *buf, size_t len) |
| { |
| eap_send_response(eap, eap->method->request_type, buf, len); |
| } |
| |
| void eap_method_new_request(struct eap_state *eap, uint8_t *buf, size_t len) |
| { |
| buf[4] = eap->method->request_type; |
| |
| if (eap->method->request_type == EAP_TYPE_EXPANDED) { |
| memcpy(buf + 5, eap->method->vendor_id, 3); |
| l_put_be32(eap->method->vendor_type, buf + 8); |
| } |
| |
| /* TODO: Save copy for retransmissions, set timer, increment counter */ |
| eap_send_packet(eap, EAP_CODE_REQUEST, ++eap->last_id, buf, len); |
| } |
| |
| static void eap_complete_timeout(struct l_timeout *timeout, void *user_data) |
| { |
| struct eap_state *eap = user_data; |
| |
| eap->complete_timeout = NULL; |
| |
| eap->complete(eap->method_success ? EAP_RESULT_SUCCESS : |
| EAP_RESULT_TIMEOUT, eap->user_data); |
| } |
| |
| void eap_start_complete_timeout(struct eap_state *eap) |
| { |
| if (eap->complete_timeout) |
| l_timeout_remove(eap->complete_timeout); |
| |
| eap->complete_timeout = l_timeout_create(5, eap_complete_timeout, |
| eap, NULL); |
| } |
| |
| static void eap_send_identity_response(struct eap_state *eap, char *identity) |
| { |
| int len = identity ? strlen(identity) : 0; |
| uint8_t buf[5 + len]; |
| |
| if (!identity) |
| identity = ""; |
| |
| memcpy(buf + 5, identity, len); |
| |
| eap_send_response(eap, EAP_TYPE_IDENTITY, buf, len + 5); |
| } |
| |
| /* To be used in authenticator mode to trigger our first request */ |
| void eap_start(struct eap_state *eap) |
| { |
| uint8_t buf[5]; |
| |
| L_WARN_ON(!eap->method || !eap->authenticator); |
| |
| /* |
| * Until we've received the Identity response we can resend the |
| * Identity request with a constant ID on EAPoL-Start. |
| */ |
| if (eap->identity) |
| return; |
| |
| eap->last_id = 1; |
| buf[4] = EAP_TYPE_IDENTITY; |
| eap_send_packet(eap, EAP_CODE_REQUEST, eap->last_id, buf, 5); |
| } |
| |
| void __eap_handle_request(struct eap_state *eap, uint16_t id, |
| const uint8_t *pkt, size_t len) |
| { |
| enum eap_type type; |
| uint8_t buf[10]; |
| int buf_len; |
| bool retransmit; |
| |
| if (len < 1) |
| /* Invalid packets to be ignored */ |
| return; |
| |
| type = pkt[0]; |
| if (type >= __EAP_TYPE_MIN_METHOD && !eap->method) { |
| l_warn("EAP server tried method %i while client had no method " |
| "configured", type); |
| |
| goto unsupported_method; |
| } |
| |
| retransmit = id == eap->last_id ? true : false; |
| eap->last_id = id; |
| |
| if (type >= __EAP_TYPE_MIN_METHOD) { |
| void (*op)(struct eap_state *eap, |
| const uint8_t *pkt, size_t len); |
| |
| eap->method_req_count++; |
| |
| if (type != eap->method->request_type) { |
| l_warn("EAP server tried method %i while client was " |
| "configured for method %i", |
| type, eap->method->request_type); |
| |
| goto unsupported_method; |
| } |
| |
| op = retransmit && eap->method->handle_retransmit ? |
| eap->method->handle_retransmit : |
| eap->method->handle_request; |
| |
| if (type != EAP_TYPE_EXPANDED) { |
| op(eap, pkt + 1, len - 1); |
| return; |
| } |
| |
| /* |
| * TODO: Handle Expanded Nak if our vendor-id / vendor-types |
| * don't match |
| */ |
| if (len < 8) |
| return; |
| |
| op(eap, pkt + 8, len - 8); |
| return; |
| } |
| |
| switch (type) { |
| case EAP_TYPE_IDENTITY: |
| eap->identity_req_count++; |
| |
| if (len >= 2) |
| l_debug("Optional EAP server identity prompt: \"%.*s\"", |
| (int) len - 1, pkt + 1); |
| |
| eap_send_identity_response(eap, eap->identity); |
| |
| return; |
| |
| case EAP_TYPE_NOTIFICATION: |
| if (len < 2) |
| /* Invalid packets to be ignored */ |
| return; |
| |
| l_warn("EAP notification: \"%.*s\"", (int) len - 1, pkt + 1); |
| |
| eap_send_response(eap, EAP_TYPE_NOTIFICATION, buf, 5); |
| |
| return; |
| |
| default: |
| unsupported_method: |
| if (!eap->method) { |
| l_info("Received an unhandled EAP packet:"); |
| l_util_hexdump(true, pkt, len, dump_eap, "[EAP] "); |
| } |
| |
| /* Send a legacy NAK response */ |
| buf_len = 5; |
| |
| /* |
| * RFC3748, Section 5.3.1: "A peer supporting Expanded Types |
| * that receives a Request for an unacceptable authentication |
| * Type (4-253,255) MAY include the value 254 in the Nak |
| * Response (Type 3) to indicate the desire for an Expanded |
| * authentication Type." |
| */ |
| buf[buf_len++] = eap->method ? eap->method->request_type : 0; |
| |
| eap_send_response(eap, EAP_TYPE_NAK, buf, buf_len); |
| return; |
| } |
| } |
| |
| static const char *eap_type_to_str(enum eap_type type, uint32_t vendor_id, |
| uint32_t vendor_type) |
| { |
| static char buf[100]; |
| |
| /* |
| * Show vendor ID if type is expanded unless vendor_id and |
| * vendor_type are 0 which means the peer is requesting an |
| * expanded type through a Legacy Nak. A Legacy Nak won't let |
| * the peer specify the which expanded type it wants. |
| * |
| * TODO: Could also use names for known methods and/or vendors. |
| */ |
| if (type == EAP_TYPE_EXPANDED && vendor_id == 0 && vendor_type != 0) |
| type = vendor_type; |
| |
| if (type != EAP_TYPE_EXPANDED || (vendor_id == 0 && vendor_type == 0)) { |
| snprintf(buf, sizeof(buf), "type(%i)", type); |
| return buf; |
| } |
| |
| snprintf(buf, sizeof(buf), "vendor(%02x, %02x, %02x), type(%i)", |
| (vendor_id >> 16) & 255, (vendor_id >> 8) & 255, |
| vendor_id & 255, vendor_type); |
| return buf; |
| } |
| |
| #define IS_EXPANDED_RESPONSE(id, t) \ |
| (type == EAP_TYPE_EXPANDED && vendor_id == (id) && vendor_type == (t)) |
| |
| static void eap_handle_response(struct eap_state *eap, const uint8_t *pkt, |
| size_t len) |
| { |
| enum eap_type type; |
| uint32_t vendor_id; |
| uint32_t vendor_type; |
| enum eap_type our_type = eap->method->request_type; |
| uint32_t our_vendor_id = (eap->method->vendor_id[0] << 16) | |
| (eap->method->vendor_id[1] << 8) | |
| eap->method->vendor_id[2]; |
| uint32_t our_vendor_type = eap->method->vendor_type; |
| |
| bool response_is(enum eap_type wanted) |
| { |
| if (type == wanted) |
| return true; |
| |
| return IS_EXPANDED_RESPONSE(0, wanted); |
| } |
| |
| if (len < 1) |
| /* Invalid packets to be ignored */ |
| return; |
| |
| type = *pkt++; |
| len--; |
| |
| if (type == EAP_TYPE_EXPANDED) { |
| if (len < 7) |
| return; |
| |
| vendor_id = (pkt[0] << 16) | (pkt[1] << 8) | pkt[2]; |
| vendor_type = l_get_be32(pkt + 3); |
| pkt += 7; |
| len -= 7; |
| |
| if (vendor_id == 0 && vendor_type == EAP_TYPE_NAK && |
| our_type != EAP_TYPE_EXPANDED) |
| /* |
| * RFC3748 5.3.2: "[The Expanded Nak Type] MUST |
| * be sent only in reply to a Request of Type 254. |
| */ |
| return; |
| } |
| |
| if (response_is(EAP_TYPE_NAK)) { |
| l_debug("EAP peer not configured for method: %s", |
| eap_type_to_str(our_type, our_vendor_id, |
| our_vendor_type)); |
| |
| if ((type == EAP_TYPE_NAK && len == 1 && pkt[0] == 0) || |
| (type != EAP_TYPE_NAK && len == 8 && |
| l_memeqzero(pkt, 8))) |
| l_debug("EAP peer proposed no alternative methods"); |
| else if (type == EAP_TYPE_NAK) |
| while (len) { |
| l_debug("EAP peer proposed method: %s", |
| eap_type_to_str(*pkt++, 0, 0)); |
| len--; |
| } |
| else |
| while (len >= 8) { |
| uint32_t v_id = (pkt[1] << 16) | |
| (pkt[2] << 8) | |
| pkt[3]; |
| |
| l_debug("EAP peer proposed method: %s", |
| eap_type_to_str(pkt[0], v_id, |
| l_get_be32(pkt + 4))); |
| pkt += 8; |
| len -= 8; |
| } |
| |
| goto unsupported_method; |
| } |
| |
| /* |
| * If we don't have peer's identity yet it means we've only sent the |
| * Identity Request so far so we expect an Identity Response and |
| * nothing else. Otherwise we only accept Response types matching |
| * the method type and we forward responses directly to the method. |
| */ |
| |
| if (!eap->identity) { |
| if (!response_is(EAP_TYPE_IDENTITY)) |
| goto unsupported_method; |
| |
| /* |
| * RFC3748 Section 5.1: "The Identity Response field MUST NOT be |
| * null terminated." |
| * Only the Request can have data after a NUL char. |
| */ |
| if (len > 253 && memchr(pkt, '\0', len)) |
| goto error; |
| |
| eap->identity = l_strndup((char *) pkt, len); |
| if (eap->method->validate_identity && |
| !eap->method->validate_identity(eap, |
| eap->identity)) |
| goto error; |
| |
| return; |
| } |
| |
| /* |
| * RFC3748 5.7: "An implementation that supports the Expanded |
| * attribute MUST treat EAP Types that are less than 256 equivalently, |
| * whether they appear as a single octet or as the 32-bit Vendor-Type |
| * within an Expanded Type where Vendor-Id is 0." |
| * (with the exception of the Nak) |
| */ |
| if (our_type != EAP_TYPE_EXPANDED) { |
| if (response_is(our_type)) |
| goto handle_response; |
| } else if (IS_EXPANDED_RESPONSE(our_vendor_id, our_vendor_type)) |
| goto handle_response; |
| |
| error: |
| unsupported_method: |
| eap_method_error(eap); |
| return; |
| |
| handle_response: |
| eap->method->handle_response(eap, pkt, len); |
| } |
| |
| void eap_rx_packet(struct eap_state *eap, const uint8_t *pkt, size_t len) |
| { |
| uint8_t code, id; |
| uint16_t eap_len; |
| |
| if (len < 4 || l_get_be16(&pkt[2]) < 4 || len < l_get_be16(&pkt[2])) |
| /* Invalid packets to be silently discarded */ |
| return; |
| |
| code = pkt[0]; |
| id = pkt[1]; |
| eap_len = l_get_be16(&pkt[2]); |
| |
| switch ((enum eap_code) code) { |
| case EAP_CODE_REQUEST: |
| if (eap->authenticator) |
| goto invalid; |
| |
| __eap_handle_request(eap, id, pkt + 4, eap_len - 4); |
| return; |
| |
| case EAP_CODE_RESPONSE: |
| if (!eap->authenticator || id != eap->last_id || !eap->method) |
| goto invalid; |
| |
| eap_handle_response(eap, pkt + 4, eap_len - 4); |
| return; |
| |
| case EAP_CODE_FAILURE: |
| case EAP_CODE_SUCCESS: |
| if (eap->authenticator) |
| goto invalid; |
| |
| if (eap->discard_success_and_failure) |
| return; |
| |
| l_timeout_remove(eap->complete_timeout); |
| eap->complete_timeout = NULL; |
| |
| /* RFC3748, Section 4.2 |
| * |
| * The Identifier field of the Success and Failure packets |
| * MUST match the Identifier field of the Response packet that |
| * it is sent in response to. However, many currently deployed |
| * implementations ignore this rule and increment Identity for |
| * the Success and Failure packets. In order to support |
| * interoperability with these products we validate id against |
| * eap->last_id and its incremented value. |
| */ |
| if (id != eap->last_id && id != eap->last_id + 1) |
| return; |
| |
| if (eap_len != 4) |
| goto invalid; |
| |
| if (code == EAP_CODE_SUCCESS && !eap->method_success) |
| /* "Canned" success packets to be discarded */ |
| return; |
| |
| if (code == EAP_CODE_FAILURE && eap->method_success) |
| /* |
| * "On the peer, after success result indications have |
| * been exchanged by both sides, a Failure packet MUST |
| * be silently discarded." |
| * |
| * "Where the peer authenticates successfully to the |
| * authenticator, but the authenticator does not send |
| * a result indication, the authenticator MAY deny |
| * access by sending a Failure packet where the peer |
| * is not currently authorized for network access." |
| * -- eap->method_success implies we've received |
| * a full result indication. |
| */ |
| return; |
| |
| eap->complete(code == EAP_CODE_SUCCESS ? EAP_RESULT_SUCCESS : |
| EAP_RESULT_FAIL, eap->user_data); |
| return; |
| |
| invalid: |
| default: |
| /* Invalid packets to be silently discarded */ |
| return; |
| } |
| } |
| |
| bool eap_secret_info_match(const void *a, const void *b) |
| { |
| const struct eap_secret_info *s = a; |
| |
| return !strcmp(s->id, b); |
| } |
| |
| void eap_append_secret(struct l_queue **out_missing, enum eap_secret_type type, |
| const char *id, const char *id2, const char *parameter, |
| enum eap_secret_cache_policy cache_policy) |
| { |
| struct eap_secret_info *info; |
| |
| if (!*out_missing) |
| *out_missing = l_queue_new(); |
| |
| info = l_new(struct eap_secret_info, 1); |
| info->id = l_strdup(id); |
| info->id2 = l_strdup(id2); |
| info->type = type; |
| info->parameter = l_strdup(parameter); |
| info->cache_policy = cache_policy; |
| l_queue_push_tail(*out_missing, info); |
| } |
| |
| void eap_secret_info_free(void *data) |
| { |
| struct eap_secret_info *info = data; |
| |
| if (!info) |
| return; |
| |
| if (info->value) { |
| size_t value_len = strlen(info->value) + 1; |
| |
| if (info->type == EAP_SECRET_REMOTE_USER_PASSWORD) |
| value_len += strlen(info->value + value_len); |
| |
| explicit_bzero(info->value, value_len); |
| l_free(info->value); |
| } |
| |
| if (info->parameter) { |
| explicit_bzero(info->parameter, strlen(info->parameter)); |
| l_free(info->parameter); |
| } |
| |
| l_free(info->id); |
| l_free(info->id2); |
| l_free(info); |
| } |
| |
| int __eap_check_settings(struct l_settings *settings, struct l_queue *secrets, |
| const char *prefix, bool set_key_material, |
| struct l_queue **missing) |
| { |
| char setting[64]; |
| const char *method_name; |
| const struct l_queue_entry *entry; |
| struct eap_method *method; |
| int ret = 0; |
| |
| snprintf(setting, sizeof(setting), "%sMethod", prefix); |
| method_name = l_settings_get_value(settings, "Security", setting); |
| |
| if (!method_name) { |
| l_error("Property %s missing", setting); |
| return -ENOENT; |
| } |
| |
| for (entry = l_queue_get_entries(eap_methods); entry; |
| entry = entry->next) { |
| method = entry->data; |
| |
| if (!strcasecmp(method_name, method->name)) |
| break; |
| } |
| |
| if (!entry) { |
| l_error("EAP method \"%s\" unsupported", method_name); |
| return -ENOTSUP; |
| } |
| |
| /* Check if selected method is suitable for 802.1x */ |
| if (set_key_material && !method->exports_msk) { |
| l_error("EAP method \"%s\" doesn't export key material", |
| method_name); |
| return -ENOTSUP; |
| } |
| |
| if (method->check_settings) { |
| ret = method->check_settings(settings, secrets, |
| prefix, missing); |
| |
| if (ret < 0) |
| return ret; |
| } |
| |
| /* |
| * Individual methods are responsible for ensuring, inside their |
| * check_settings(), that they have enough data to return the |
| * identity after load_settings() if it is required. |
| */ |
| |
| return 0; |
| } |
| |
| int eap_check_settings(struct l_settings *settings, struct l_queue *secrets, |
| const char *prefix, bool set_key_material, |
| struct l_queue **out_missing) |
| { |
| struct l_queue *missing = NULL; |
| int ret = __eap_check_settings(settings, secrets, prefix, |
| set_key_material, &missing); |
| |
| if (ret < 0) { |
| l_queue_destroy(missing, eap_secret_info_free); |
| return ret; |
| } |
| |
| if (missing && l_queue_isempty(missing)) { |
| l_queue_destroy(missing, NULL); |
| missing = NULL; |
| } |
| |
| *out_missing = missing; |
| return 0; |
| } |
| |
| bool eap_load_settings(struct eap_state *eap, struct l_settings *settings, |
| const char *prefix) |
| { |
| char setting[64]; |
| const char *method_name; |
| const struct l_queue_entry *entry; |
| struct eap_method *method; |
| |
| snprintf(setting, sizeof(setting), "%sMethod", prefix); |
| method_name = l_settings_get_value(settings, "Security", setting); |
| |
| if (!method_name) |
| return false; |
| |
| for (entry = l_queue_get_entries(eap_methods); entry; |
| entry = entry->next) { |
| method = entry->data; |
| |
| if (!strcasecmp(method_name, method->name)) { |
| eap->method = method; |
| |
| break; |
| } |
| } |
| |
| if (!eap->method) |
| return false; |
| |
| /* Check if selected method is suitable for 802.1x */ |
| if (eap->set_key_material && !eap->method->exports_msk) |
| goto err; |
| |
| if (eap->method->load_settings) |
| if (!eap->method->load_settings(eap, settings, prefix)) |
| goto err; |
| |
| eap->authenticator = eap->method->handle_request == NULL; |
| |
| if (eap->authenticator) |
| return true; |
| |
| /* get identity from settings or from EAP method */ |
| if (!eap->method->get_identity) { |
| snprintf(setting, sizeof(setting), "%sIdentity", prefix); |
| eap->identity = l_settings_get_string(settings, |
| "Security", setting); |
| eap->identity_setting = l_strdup(setting); |
| } else { |
| eap->identity = l_strdup(eap->method->get_identity(eap)); |
| } |
| |
| /* |
| * RFC 4282 Section 2.2 - NAI Length Considerations |
| * |
| * Devices handling NAIs MUST support an NAI length of at least 72 |
| * octets. Support for an NAI length of 253 octets is RECOMMENDED. |
| * ... |
| * RADIUS is unable to support NAI lengths beyond 253 octets |
| * |
| * We also need to fail if the identity is too large for the set MTU |
| * size minus 5 (header). |
| */ |
| if (eap->identity) { |
| size_t id_len = strlen(eap->identity); |
| |
| if (id_len > 253 || id_len > eap->mtu - 5) { |
| l_error("Identity is too long"); |
| goto err; |
| } |
| } |
| |
| return true; |
| |
| err: |
| eap_free_common(eap); |
| |
| return false; |
| } |
| |
| void eap_set_data(struct eap_state *eap, void *data) |
| { |
| eap->method_state = data; |
| } |
| |
| void *eap_get_data(struct eap_state *eap) |
| { |
| return eap->method_state; |
| } |
| |
| enum eap_type eap_get_method_type(struct eap_state *eap) |
| { |
| return eap->method->request_type; |
| } |
| |
| const char *eap_get_method_name(struct eap_state *eap) |
| { |
| return eap->method->name; |
| } |
| |
| void eap_set_key_material(struct eap_state *eap, |
| const uint8_t *msk_data, size_t msk_len, |
| const uint8_t *emsk_data, size_t emsk_len, |
| const uint8_t *iv, size_t iv_len, |
| const uint8_t *session_id, size_t session_len) |
| { |
| if (!eap->set_key_material) |
| return; |
| |
| eap->set_key_material(msk_data, msk_len, emsk_data, emsk_len, |
| iv, iv_len, session_id, session_len, |
| eap->user_data); |
| } |
| |
| void eap_method_event(struct eap_state *eap, unsigned int id, const void *data) |
| { |
| if (!eap->event_func) |
| return; |
| |
| eap->event_func(id, data, eap->user_data); |
| } |
| |
| bool eap_method_is_success(struct eap_state *eap) |
| { |
| return eap->method_success; |
| } |
| |
| void eap_method_success(struct eap_state *eap) |
| { |
| eap->method_success = true; |
| |
| if (eap->authenticator) { |
| uint8_t buf[4]; |
| |
| /* ID not incremented for Success or Failure (RFC3748 4.2) */ |
| eap_send_packet(eap, EAP_CODE_FAILURE, eap->last_id, buf, 4); |
| eap->complete(EAP_RESULT_SUCCESS, eap->user_data); |
| } |
| } |
| |
| void eap_discard_success_and_failure(struct eap_state *eap, bool discard) |
| { |
| eap->discard_success_and_failure = discard; |
| } |
| |
| void eap_method_error(struct eap_state *eap) |
| { |
| if (eap->authenticator) { |
| uint8_t buf[4]; |
| |
| /* ID not incremented for Success or Failure (RFC3748 4.2) */ |
| eap_send_packet(eap, EAP_CODE_FAILURE, eap->last_id, buf, 4); |
| } |
| |
| /* |
| * It looks like neither EAP nor EAP-TLS specify the error handling |
| * behavior. |
| */ |
| eap->complete(EAP_RESULT_FAIL, eap->user_data); |
| } |
| |
| void eap_save_last_id(struct eap_state *eap, uint8_t *last_id) |
| { |
| *last_id = eap->last_id; |
| } |
| |
| void eap_restore_last_id(struct eap_state *eap, uint8_t last_id) |
| { |
| eap->last_id = last_id; |
| } |
| |
| int eap_register_method(struct eap_method *method) |
| { |
| if (!method->handle_request && !method->handle_response) |
| return -EPERM; |
| |
| l_queue_push_head(eap_methods, method); |
| return 0; |
| } |
| |
| int eap_unregister_method(struct eap_method *method) |
| { |
| bool r; |
| |
| r = l_queue_remove(eap_methods, method); |
| if (r) |
| return 0; |
| |
| return -ENOENT; |
| } |
| |
| void __eap_set_config(struct l_settings *config) |
| { |
| if (!l_settings_get_uint(config, "EAP", "MTU", &default_mtu)) |
| default_mtu = 1400; /* on WiFi the real MTU is around 2304 */ |
| } |
| |
| static void __eap_method_enable(struct eap_method_desc *start, |
| struct eap_method_desc *stop) |
| { |
| struct eap_method_desc *desc; |
| |
| l_debug(""); |
| |
| if (start == NULL || stop == NULL) |
| return; |
| |
| for (desc = start; desc < stop; desc++) { |
| if (!desc->init) |
| continue; |
| |
| desc->init(); |
| } |
| } |
| |
| static void __eap_method_disable(struct eap_method_desc *start, |
| struct eap_method_desc *stop) |
| { |
| struct eap_method_desc *desc; |
| |
| l_debug(""); |
| |
| if (start == NULL || stop == NULL) |
| return; |
| |
| for (desc = start; desc < stop; desc++) { |
| if (!desc->exit) |
| continue; |
| |
| desc->exit(); |
| } |
| } |
| |
| extern struct eap_method_desc __start___eap[]; |
| extern struct eap_method_desc __stop___eap[]; |
| |
| int eap_init(void) |
| { |
| eap_methods = l_queue_new(); |
| __eap_method_enable(__start___eap, __stop___eap); |
| |
| /* |
| * RFC 3748, Section 3.1, [4], "Minimum MTU": |
| * EAP is capable of functioning on lower layers that |
| * provide an EAP MTU size of 1020 octets or greater. |
| */ |
| if (default_mtu == 0) |
| default_mtu = 1020; |
| |
| return 0; |
| } |
| |
| void eap_exit(void) |
| { |
| __eap_method_disable(__start___eap, __stop___eap); |
| l_queue_destroy(eap_methods, NULL); |
| } |
| |
| IWD_MODULE(eap, eap_init, eap_exit); |