Compare commits

...

3 Commits

Author SHA1 Message Date
2ae26de3ec [eap] Add support for the MD5-Challenge authentication type
RFC 3748 states that support for MD5-Challenge is mandatory for EAP
implementations.  The MD5 and CHAP code is already included in the
default build since it is required by iSCSI, and so this does not
substantially increase the binary size.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 15:33:49 +00:00
6db299dc0f [eap] Add support for sending an EAP identity
Allow the ${netX/username} setting to be used to specify an EAP
identity to be returned in response to a Request-Identity, and provide
a mechanism for responding with a NAK to indicate which authentication
types we support.

If no identity is specified then fall back to the current behaviour of
not sending any Request-Identity response, so that switches will time
out and switch to MAC Authentication Bypass (MAB) if applicable.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 15:30:36 +00:00
dc15c6e633 [eap] Ignore any received EAP responses
EAP responses (including our own) may be forwarded by switches but are
not of interest to us and can be safely ignored if received.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2024-01-10 15:28:59 +00:00
2 changed files with 275 additions and 26 deletions

View File

@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/netdevice.h>
#include <ipxe/timer.h>
#include <ipxe/tables.h>
/** EAP header */
struct eap_header {
@ -26,17 +27,39 @@ struct eap_header {
/** EAP request */
#define EAP_CODE_REQUEST 1
/** EAP request */
struct eap_request {
/** EAP response */
#define EAP_CODE_RESPONSE 2
/** EAP request/response message */
struct eap_message {
/** Header */
struct eap_header hdr;
/** Type */
uint8_t type;
/** Type data */
uint8_t data[0];
} __attribute__ (( packed ));
/** EAP "no available types" marker */
#define EAP_TYPE_NONE 0
/** EAP identity */
#define EAP_TYPE_IDENTITY 1
/** EAP NAK */
#define EAP_TYPE_NAK 3
/** EAP MD5 challenge request/response */
#define EAP_TYPE_MD5 4
/** EAP MD5 challenge request/response type data */
struct eap_md5 {
/** Value length */
uint8_t len;
/** Value */
uint8_t value[0];
} __attribute__ (( packed ));
/** EAP success */
#define EAP_CODE_SUCCESS 3
@ -47,8 +70,8 @@ struct eap_request {
union eap_packet {
/** Header */
struct eap_header hdr;
/** Request */
struct eap_request req;
/** Request/response message */
struct eap_message msg;
};
/** EAP link block timeout
@ -87,7 +110,11 @@ struct eap_supplicant {
/** Network device */
struct net_device *netdev;
/** Flags */
unsigned int flags;
uint16_t flags;
/** ID for current request/response */
uint8_t id;
/** Type for current request/response */
uint8_t type;
/**
* Transmit EAP response
*
@ -117,6 +144,28 @@ struct eap_supplicant {
*/
#define EAP_FL_PASSIVE 0x0002
/** An EAP method */
struct eap_method {
/** Type */
uint8_t type;
/**
* Handle EAP request
*
* @v supplicant EAP supplicant
* @v req Request type data
* @v req_len Length of request type data
* @ret rc Return status code
*/
int ( * rx ) ( struct eap_supplicant *supplicant,
const void *req, size_t req_len );
};
/** EAP method table */
#define EAP_METHODS __table ( struct eap_method, "eap_methods" )
/** Declare an EAP method */
#define __eap_method __table_entry ( EAP_METHODS, 01 )
extern int eap_rx ( struct eap_supplicant *supplicant,
const void *data, size_t len );

View File

@ -23,8 +23,13 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/md5.h>
#include <ipxe/chap.h>
#include <ipxe/eap.h>
/** @file
@ -34,59 +39,251 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
* Handle EAP Request-Identity
* Transmit EAP response
*
* @v supplicant EAP supplicant
* @v rsp Response type data
* @v rsp_len Length of response type data
* @ret rc Return status code
*/
static int eap_tx_response ( struct eap_supplicant *supplicant,
const void *rsp, size_t rsp_len ) {
struct net_device *netdev = supplicant->netdev;
struct eap_message *msg;
size_t len;
int rc;
/* Allocate and populate response */
len = ( sizeof ( *msg ) + rsp_len );
msg = malloc ( len );
if ( ! msg ) {
rc = -ENOMEM;
goto err_alloc;
}
msg->hdr.code = EAP_CODE_RESPONSE;
msg->hdr.id = supplicant->id;
msg->hdr.len = htons ( len );
msg->type = supplicant->type;
memcpy ( msg->data, rsp, rsp_len );
/* Transmit response */
if ( ( rc = supplicant->tx ( supplicant, msg, len ) ) != 0 ) {
DBGC ( netdev, "EAP %s could not transmit: %s\n",
netdev->name, strerror ( rc ) );
goto err_tx;
}
err_tx:
free ( msg );
err_alloc:
return rc;
}
/**
* Transmit EAP NAK
*
* @v supplicant EAP supplicant
* @ret rc Return status code
*/
static int eap_rx_request_identity ( struct eap_supplicant *supplicant ) {
static int eap_tx_nak ( struct eap_supplicant *supplicant ) {
unsigned int max = table_num_entries ( EAP_METHODS );
uint8_t methods[ max + 1 /* potential EAP_TYPE_NONE */ ];
unsigned int count = 0;
struct eap_method *method;
/* Populate methods list */
for_each_table_entry ( method, EAP_METHODS ) {
if ( method->type > EAP_TYPE_NAK )
methods[count++] = method->type;
}
if ( ! count )
methods[count++] = EAP_TYPE_NONE;
assert ( count <= max );
/* Transmit response */
supplicant->type = EAP_TYPE_NAK;
return eap_tx_response ( supplicant, methods, count );
}
/**
* Handle EAP Request-Identity
*
* @v supplicant EAP supplicant
* @v req Request type data
* @v req_len Length of request type data
* @ret rc Return status code
*/
static int eap_rx_identity ( struct eap_supplicant *supplicant,
const void *req, size_t req_len ) {
struct net_device *netdev = supplicant->netdev;
void *rsp;
int rsp_len;
int rc;
/* Treat Request-Identity as blocking the link */
DBGC ( netdev, "EAP %s Request-Identity blocking link\n",
netdev->name );
DBGC_HDA ( netdev, 0, req, req_len );
netdev_link_block ( netdev, EAP_BLOCK_TIMEOUT );
/* Mark EAP as in progress */
supplicant->flags |= EAP_FL_ONGOING;
/* We have no identity to offer, so wait until the switch
* times out and switches to MAC Authentication Bypass (MAB).
*/
supplicant->flags |= EAP_FL_PASSIVE;
/* Construct response, if applicable */
rsp_len = fetch_raw_setting_copy ( netdev_settings ( netdev ),
&username_setting, &rsp );
if ( rsp_len < 0 ) {
/* We have no identity to offer, so wait until the
* switch times out and switches to MAC Authentication
* Bypass (MAB).
*/
DBGC2 ( netdev, "EAP %s has no identity\n", netdev->name );
supplicant->flags |= EAP_FL_PASSIVE;
rc = 0;
goto no_response;
}
return 0;
/* Transmit response */
if ( ( rc = eap_tx_response ( supplicant, rsp, rsp_len ) ) != 0 )
goto err_tx;
err_tx:
free ( rsp );
no_response:
return rc;
}
/** EAP Request-Identity method */
struct eap_method eap_identity_method __eap_method = {
.type = EAP_TYPE_IDENTITY,
.rx = eap_rx_identity,
};
/**
* Handle EAP Challenge-MD5
*
* @v req Request type data
* @v req_len Length of request type data
* @ret rc Return status code
*/
static int eap_rx_md5 ( struct eap_supplicant *supplicant,
const void *req, size_t req_len ) {
struct net_device *netdev = supplicant->netdev;
const struct eap_md5 *md5req = req;
struct {
uint8_t len;
uint8_t value[MD5_DIGEST_SIZE];
} __attribute__ (( packed )) md5rsp;
struct chap_response chap;
void *secret;
int secret_len;
int rc;
/* Sanity checks */
if ( req_len < sizeof ( *md5req ) ) {
DBGC ( netdev, "EAP %s underlength Challenge-MD5:\n",
netdev->name );
DBGC_HDA ( netdev, 0, req, req_len );
rc = -EINVAL;
goto err_sanity;
}
if ( ( req_len - sizeof ( *md5req ) ) < md5req->len ) {
DBGC ( netdev, "EAP %s truncated Challenge-MD5:\n",
netdev->name );
DBGC_HDA ( netdev, 0, req, req_len );
rc = -EINVAL;
goto err_sanity;
}
/* Construct response */
if ( ( rc = chap_init ( &chap, &md5_algorithm ) ) != 0 ) {
DBGC ( netdev, "EAP %s could not initialise CHAP: %s\n",
netdev->name, strerror ( rc ) );
goto err_chap;
}
chap_set_identifier ( &chap, supplicant->id );
secret_len = fetch_raw_setting_copy ( netdev_settings ( netdev ),
&password_setting, &secret );
if ( secret_len < 0 ) {
rc = secret_len;
DBGC ( netdev, "EAP %s has no secret: %s\n",
netdev->name, strerror ( rc ) );
goto err_secret;
}
chap_update ( &chap, secret, secret_len );
chap_update ( &chap, md5req->value, md5req->len );
chap_respond ( &chap );
assert ( chap.response_len == sizeof ( md5rsp.value ) );
md5rsp.len = sizeof ( md5rsp.value );
memcpy ( md5rsp.value, chap.response, sizeof ( md5rsp.value ) );
/* Transmit response */
if ( ( rc = eap_tx_response ( supplicant, &md5rsp,
sizeof ( md5rsp ) ) ) != 0 )
goto err_tx;
err_tx:
free ( secret );
err_secret:
chap_finish ( &chap );
err_chap:
err_sanity:
return rc;
}
/** EAP Challenge-MD5 method */
struct eap_method eap_md5_method __eap_method = {
.type = EAP_TYPE_MD5,
.rx = eap_rx_md5,
};
/**
* Handle EAP Request
*
* @v supplicant EAP supplicant
* @v req EAP request
* @v msg EAP request
* @v len Length of EAP request
* @ret rc Return status code
*/
static int eap_rx_request ( struct eap_supplicant *supplicant,
const struct eap_request *req, size_t len ) {
const struct eap_message *msg, size_t len ) {
struct net_device *netdev = supplicant->netdev;
struct eap_method *method;
const void *req;
size_t req_len;
/* Sanity check */
if ( len < sizeof ( *req ) ) {
/* Sanity checks */
if ( len < sizeof ( *msg ) ) {
DBGC ( netdev, "EAP %s underlength request:\n", netdev->name );
DBGC_HDA ( netdev, 0, req, len );
DBGC_HDA ( netdev, 0, msg, len );
return -EINVAL;
}
if ( len < ntohs ( msg->hdr.len ) ) {
DBGC ( netdev, "EAP %s truncated request:\n", netdev->name );
DBGC_HDA ( netdev, 0, msg, len );
return -EINVAL;
}
req = msg->data;
req_len = ( ntohs ( msg->hdr.len ) - sizeof ( *msg ) );
/* Record request details */
supplicant->id = msg->hdr.id;
supplicant->type = msg->type;
/* Handle according to type */
switch ( req->type ) {
case EAP_TYPE_IDENTITY:
return eap_rx_request_identity ( supplicant );
default:
DBGC ( netdev, "EAP %s requested type %d unknown:\n",
netdev->name, req->type );
DBGC_HDA ( netdev, 0, req, len );
return -ENOTSUP;
for_each_table_entry ( method, EAP_METHODS ) {
if ( msg->type == method->type )
return method->rx ( supplicant, req, req_len );
}
DBGC ( netdev, "EAP %s requested type %d unknown:\n",
netdev->name, msg->type );
DBGC_HDA ( netdev, 0, msg, len );
/* Send NAK if applicable */
if ( msg->type > EAP_TYPE_NAK )
return eap_tx_nak ( supplicant );
return -ENOTSUP;
}
/**
@ -148,7 +345,10 @@ int eap_rx ( struct eap_supplicant *supplicant, const void *data,
/* Handle according to code */
switch ( eap->hdr.code ) {
case EAP_CODE_REQUEST:
return eap_rx_request ( supplicant, &eap->req, len );
return eap_rx_request ( supplicant, &eap->msg, len );
case EAP_CODE_RESPONSE:
DBGC2 ( netdev, "EAP %s ignoring response\n", netdev->name );
return 0;
case EAP_CODE_SUCCESS:
return eap_rx_success ( supplicant );
case EAP_CODE_FAILURE: