[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>
pull/118/head
Michael Brown 2024-01-10 15:30:36 +00:00
parent 0abb3e85e5
commit c6226f104e
2 changed files with 178 additions and 26 deletions

View File

@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h> #include <stdint.h>
#include <ipxe/netdevice.h> #include <ipxe/netdevice.h>
#include <ipxe/timer.h> #include <ipxe/timer.h>
#include <ipxe/tables.h>
/** EAP header */ /** EAP header */
struct eap_header { struct eap_header {
@ -29,17 +30,25 @@ struct eap_header {
/** EAP response */ /** EAP response */
#define EAP_CODE_RESPONSE 2 #define EAP_CODE_RESPONSE 2
/** EAP request */ /** EAP request/response message */
struct eap_request { struct eap_message {
/** Header */ /** Header */
struct eap_header hdr; struct eap_header hdr;
/** Type */ /** Type */
uint8_t type; uint8_t type;
/** Type data */
uint8_t data[0];
} __attribute__ (( packed )); } __attribute__ (( packed ));
/** EAP "no available types" marker */
#define EAP_TYPE_NONE 0
/** EAP identity */ /** EAP identity */
#define EAP_TYPE_IDENTITY 1 #define EAP_TYPE_IDENTITY 1
/** EAP NAK */
#define EAP_TYPE_NAK 3
/** EAP success */ /** EAP success */
#define EAP_CODE_SUCCESS 3 #define EAP_CODE_SUCCESS 3
@ -50,8 +59,8 @@ struct eap_request {
union eap_packet { union eap_packet {
/** Header */ /** Header */
struct eap_header hdr; struct eap_header hdr;
/** Request */ /** Request/response message */
struct eap_request req; struct eap_message msg;
}; };
/** EAP link block timeout /** EAP link block timeout
@ -90,7 +99,11 @@ struct eap_supplicant {
/** Network device */ /** Network device */
struct net_device *netdev; struct net_device *netdev;
/** Flags */ /** 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 * Transmit EAP response
* *
@ -120,6 +133,28 @@ struct eap_supplicant {
*/ */
#define EAP_FL_PASSIVE 0x0002 #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, extern int eap_rx ( struct eap_supplicant *supplicant,
const void *data, size_t len ); const void *data, size_t len );

View File

@ -23,7 +23,10 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <byteswap.h>
#include <ipxe/netdevice.h> #include <ipxe/netdevice.h>
#include <ipxe/eap.h> #include <ipxe/eap.h>
@ -34,59 +37,173 @@ 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 * @v supplicant EAP supplicant
* @ret rc Return status code * @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; struct net_device *netdev = supplicant->netdev;
void *rsp;
int rsp_len;
int rc;
/* Treat Request-Identity as blocking the link */ /* Treat Request-Identity as blocking the link */
DBGC ( netdev, "EAP %s Request-Identity blocking link\n", DBGC ( netdev, "EAP %s Request-Identity blocking link\n",
netdev->name ); netdev->name );
DBGC_HDA ( netdev, 0, req, req_len );
netdev_link_block ( netdev, EAP_BLOCK_TIMEOUT ); netdev_link_block ( netdev, EAP_BLOCK_TIMEOUT );
/* Mark EAP as in progress */ /* Mark EAP as in progress */
supplicant->flags |= EAP_FL_ONGOING; supplicant->flags |= EAP_FL_ONGOING;
/* We have no identity to offer, so wait until the switch /* Construct response, if applicable */
* times out and switches to MAC Authentication Bypass (MAB). 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; 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 Request * Handle EAP Request
* *
* @v supplicant EAP supplicant * @v supplicant EAP supplicant
* @v req EAP request * @v msg EAP request
* @v len Length of EAP request * @v len Length of EAP request
* @ret rc Return status code * @ret rc Return status code
*/ */
static int eap_rx_request ( struct eap_supplicant *supplicant, 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 net_device *netdev = supplicant->netdev;
struct eap_method *method;
const void *req;
size_t req_len;
/* Sanity check */ /* Sanity checks */
if ( len < sizeof ( *req ) ) { if ( len < sizeof ( *msg ) ) {
DBGC ( netdev, "EAP %s underlength request:\n", netdev->name ); DBGC ( netdev, "EAP %s underlength request:\n", netdev->name );
DBGC_HDA ( netdev, 0, req, len ); DBGC_HDA ( netdev, 0, msg, len );
return -EINVAL; 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 */ /* Handle according to type */
switch ( req->type ) { for_each_table_entry ( method, EAP_METHODS ) {
case EAP_TYPE_IDENTITY: if ( msg->type == method->type )
return eap_rx_request_identity ( supplicant ); return method->rx ( supplicant, req, req_len );
default:
DBGC ( netdev, "EAP %s requested type %d unknown:\n",
netdev->name, req->type );
DBGC_HDA ( netdev, 0, req, len );
return -ENOTSUP;
} }
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 +265,7 @@ int eap_rx ( struct eap_supplicant *supplicant, const void *data,
/* Handle according to code */ /* Handle according to code */
switch ( eap->hdr.code ) { switch ( eap->hdr.code ) {
case EAP_CODE_REQUEST: case EAP_CODE_REQUEST:
return eap_rx_request ( supplicant, &eap->req, len ); return eap_rx_request ( supplicant, &eap->msg, len );
case EAP_CODE_RESPONSE: case EAP_CODE_RESPONSE:
DBGC2 ( netdev, "EAP %s ignoring response\n", netdev->name ); DBGC2 ( netdev, "EAP %s ignoring response\n", netdev->name );
return 0; return 0;