3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-05 03:29:24 +01:00
iwd/src/eap.c

983 lines
23 KiB
C
Raw Normal View History

/*
*
* 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"
2016-11-15 23:44:07 +01:00
static uint32_t default_mtu;
2019-08-15 19:39:27 +02:00
static struct l_queue *eap_methods;
2018-05-18 17:29:58 +02:00
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;
bool authenticator;
int last_id;
void *method_state;
bool method_success;
struct l_timeout *complete_timeout;
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;
2016-11-15 23:44:07 +01:00
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;
}
}
void eap_free(struct eap_state *eap)
{
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;
}
2019-04-10 23:52:31 +02:00
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);
}
2018-02-14 01:00:40 +01:00
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;
2016-11-29 23:31:22 +01:00
eap->last_id = id;
if (type >= __EAP_TYPE_MIN_METHOD) {
void (*op)(struct eap_state *eap,
const uint8_t *pkt, size_t len);
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) {
2016-11-29 23:31:22 +01:00
op(eap, pkt + 1, len - 1);
return;
}
/*
* TODO: Handle Expanded Nak if our vendor-id / vendor-types
* don't match
*/
if (len < 8)
return;
2016-11-29 23:31:22 +01:00
op(eap, pkt + 8, len - 8);
return;
}
switch (type) {
case EAP_TYPE_IDENTITY:
if (len >= 2)
2018-09-07 19:25:20 +02:00
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:
2018-05-18 17:29:58 +02:00
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;
}
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 uninitialized_var(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;
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 (type == EAP_TYPE_NAK ||
(type == EAP_TYPE_EXPANDED &&
vendor_id == 0 && vendor_type == 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;
}
/*
* 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 (type == EAP_TYPE_EXPANDED && vendor_id == 0)
type = vendor_type;
/*
* 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 (type != 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;
}
if (type != our_type ||
(type == EAP_TYPE_EXPANDED &&
(vendor_id != our_vendor_id ||
vendor_type != our_vendor_type)))
goto unsupported_method;
eap->method->handle_response(eap, pkt, len);
return;
error:
unsupported_method:
eap_method_error(eap);
}
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;
2018-02-14 01:00:40 +01:00
__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;
}
}
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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)
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
{
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);
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
info->type = type;
info->parameter = l_strdup(parameter);
info->cache_policy = cache_policy;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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);
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
l_free(info->value);
}
if (info->parameter) {
explicit_bzero(info->parameter, strlen(info->parameter));
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
l_free(info->parameter);
}
l_free(info->id);
l_free(info->id2);
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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)
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
{
char setting[64];
const char *method_name;
const struct l_queue_entry *entry;
struct eap_method *method;
int ret = 0;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
}
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;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
}
/* 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;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
}
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.
*/
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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;
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
}
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 */
eap: Validate settings, report passwords needed With the goal of requesting the required passwords/passphrases, such as the TLS private key passphrase, from the agent, add a static method eap_check_settings to validate the settings and calculate what passwords are needed for those settings, if any. This is separate from eap_load_settings because that can only be called later, once we've got an eap state machine object. We need to get all the needed EAP credentials from the user before we even start connecting. While we do this, we also validate the settings and output any error messages through l_error (this could be changed so the messages go somewhere else in the future), so I removed the error messages from eap_load_settings and that method now assumes that eap_check_settings has been called before. eap_check_settings calls the appropriate method's .check_settings method if the settings are complete enough to contain the method name. The policy is that any data can be provided inside the l_settings object (from the network provisioning/config file), but some of the more sensitive fields, like private key passwords, can be optionally omitted and then the UI will ask for them and iwd will be careful with caching them. Within struct eap_secret_info, "id" is mainly for the EAP method to locate the info in the list. "value" is the actual value returned by agent. "parameter" is an optional string to be passed to the agent. For a private key passphrase it may be the path to the key file, for a password it may be the username for which the password is requested.
2018-04-18 07:03:22 +02:00
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);
} 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);
}
2018-02-22 01:47:19 +01:00
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;
}
2015-11-02 16:54:12 +01:00
int eap_register_method(struct eap_method *method)
{
if (!method->handle_request && !method->handle_response)
return -EPERM;
l_queue_push_head(eap_methods, method);
2015-11-02 16:54:12 +01:00
return 0;
}
2015-11-03 04:38:45 +01:00
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)
{
2019-10-24 20:51:20 +02:00
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)
{
2017-04-19 00:29:09 +02:00
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)
{
2017-04-19 00:29:09 +02:00
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);
2016-11-15 23:44:07 +01:00
/*
* 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)
2016-11-15 23:44:07 +01:00
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);