mirror of https://github.com/ipxe/ipxe.git
[eap] Define a supplicant model for EAP and EAPoL
Extend the EAP model to include a record of whether or not EAP authentication has completed (successfully or otherwise), and to provide a method for transmitting EAP responses. Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/1052/head
parent
cac3a584dc
commit
56cc61a168
|
@ -64,6 +64,25 @@ union eap_packet {
|
|||
*/
|
||||
#define EAP_BLOCK_TIMEOUT ( 45 * TICKS_PER_SEC )
|
||||
|
||||
extern int eap_rx ( struct net_device *netdev, const void *data, size_t len );
|
||||
/** An EAP supplicant */
|
||||
struct eap_supplicant {
|
||||
/** Network device */
|
||||
struct net_device *netdev;
|
||||
/** Authentication outcome is final */
|
||||
int done;
|
||||
/**
|
||||
* Transmit EAP response
|
||||
*
|
||||
* @v supplicant EAP supplicant
|
||||
* @v data Response data
|
||||
* @v len Length of response data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * tx ) ( struct eap_supplicant *supplicant,
|
||||
const void *data, size_t len );
|
||||
};
|
||||
|
||||
extern int eap_rx ( struct eap_supplicant *supplicant,
|
||||
const void *data, size_t len );
|
||||
|
||||
#endif /* _IPXE_EAP_H */
|
||||
|
|
|
@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
#include <stdint.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/tables.h>
|
||||
#include <ipxe/eap.h>
|
||||
|
||||
/** EAPoL header */
|
||||
struct eapol_header {
|
||||
|
@ -32,6 +33,12 @@ struct eapol_header {
|
|||
/** EAPoL key */
|
||||
#define EAPOL_TYPE_KEY 5
|
||||
|
||||
/** An EAPoL supplicant */
|
||||
struct eapol_supplicant {
|
||||
/** EAP supplicant */
|
||||
struct eap_supplicant eap;
|
||||
};
|
||||
|
||||
/** An EAPoL handler */
|
||||
struct eapol_handler {
|
||||
/** Type */
|
||||
|
@ -39,15 +46,15 @@ struct eapol_handler {
|
|||
/**
|
||||
* Process received packet
|
||||
*
|
||||
* @v supplicant EAPoL supplicant
|
||||
* @v iobuf I/O buffer
|
||||
* @v netdev Network device
|
||||
* @v ll_source Link-layer source address
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This method takes ownership of the I/O buffer.
|
||||
*/
|
||||
int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev,
|
||||
const void *ll_source );
|
||||
int ( * rx ) ( struct eapol_supplicant *supplicant,
|
||||
struct io_buffer *iobuf, const void *ll_source );
|
||||
};
|
||||
|
||||
/** EAPoL handler table */
|
||||
|
|
|
@ -761,13 +761,14 @@ static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx,
|
|||
/**
|
||||
* Handle receipt of EAPOL-Key frame for WPA
|
||||
*
|
||||
* @v iob I/O buffer
|
||||
* @v netdev Network device
|
||||
* @v ll_source Source link-layer address
|
||||
* @v supplicant EAPoL supplicant
|
||||
* @v iob I/O buffer
|
||||
* @v ll_source Source link-layer address
|
||||
*/
|
||||
static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev,
|
||||
const void *ll_source )
|
||||
static int eapol_key_rx ( struct eapol_supplicant *supplicant,
|
||||
struct io_buffer *iob, const void *ll_source )
|
||||
{
|
||||
struct net_device *netdev = supplicant->eap.netdev;
|
||||
struct net80211_device *dev = net80211_get ( netdev );
|
||||
struct eapol_header *eapol;
|
||||
struct eapol_key_pkt *pkt;
|
||||
|
|
|
@ -36,10 +36,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
/**
|
||||
* Handle EAP Request-Identity
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAP supplicant
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eap_rx_request_identity ( struct net_device *netdev ) {
|
||||
static int eap_rx_request_identity ( struct eap_supplicant *supplicant ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
|
||||
/* Treat Request-Identity as blocking the link */
|
||||
DBGC ( netdev, "EAP %s Request-Identity blocking link\n",
|
||||
|
@ -52,13 +53,14 @@ static int eap_rx_request_identity ( struct net_device *netdev ) {
|
|||
/**
|
||||
* Handle EAP Request
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAP supplicant
|
||||
* @v req EAP request
|
||||
* @v len Length of EAP request
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eap_rx_request ( struct net_device *netdev,
|
||||
static int eap_rx_request ( struct eap_supplicant *supplicant,
|
||||
const struct eap_request *req, size_t len ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
|
||||
/* Sanity check */
|
||||
if ( len < sizeof ( *req ) ) {
|
||||
|
@ -67,10 +69,13 @@ static int eap_rx_request ( struct net_device *netdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Mark authentication as incomplete */
|
||||
supplicant->done = 0;
|
||||
|
||||
/* Handle according to type */
|
||||
switch ( req->type ) {
|
||||
case EAP_TYPE_IDENTITY:
|
||||
return eap_rx_request_identity ( netdev );
|
||||
return eap_rx_request_identity ( supplicant );
|
||||
default:
|
||||
DBGC ( netdev, "EAP %s requested type %d unknown:\n",
|
||||
netdev->name, req->type );
|
||||
|
@ -82,10 +87,14 @@ static int eap_rx_request ( struct net_device *netdev,
|
|||
/**
|
||||
* Handle EAP Success
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAP supplicant
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eap_rx_success ( struct net_device *netdev ) {
|
||||
static int eap_rx_success ( struct eap_supplicant *supplicant ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
|
||||
/* Mark authentication as complete */
|
||||
supplicant->done = 1;
|
||||
|
||||
/* Mark link as unblocked */
|
||||
DBGC ( netdev, "EAP %s Success\n", netdev->name );
|
||||
|
@ -97,10 +106,14 @@ static int eap_rx_success ( struct net_device *netdev ) {
|
|||
/**
|
||||
* Handle EAP Failure
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAP supplicant
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eap_rx_failure ( struct net_device *netdev ) {
|
||||
static int eap_rx_failure ( struct eap_supplicant *supplicant ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
|
||||
/* Mark authentication as complete */
|
||||
supplicant->done = 1;
|
||||
|
||||
/* Record error */
|
||||
DBGC ( netdev, "EAP %s Failure\n", netdev->name );
|
||||
|
@ -110,12 +123,14 @@ static int eap_rx_failure ( struct net_device *netdev ) {
|
|||
/**
|
||||
* Handle EAP packet
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAP supplicant
|
||||
* @v data EAP packet
|
||||
* @v len Length of EAP packet
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int eap_rx ( struct net_device *netdev, const void *data, size_t len ) {
|
||||
int eap_rx ( struct eap_supplicant *supplicant, const void *data,
|
||||
size_t len ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
const union eap_packet *eap = data;
|
||||
|
||||
/* Sanity check */
|
||||
|
@ -128,11 +143,11 @@ int eap_rx ( struct net_device *netdev, const void *data, size_t len ) {
|
|||
/* Handle according to code */
|
||||
switch ( eap->hdr.code ) {
|
||||
case EAP_CODE_REQUEST:
|
||||
return eap_rx_request ( netdev, &eap->req, len );
|
||||
return eap_rx_request ( supplicant, &eap->req, len );
|
||||
case EAP_CODE_SUCCESS:
|
||||
return eap_rx_success ( netdev );
|
||||
return eap_rx_success ( supplicant );
|
||||
case EAP_CODE_FAILURE:
|
||||
return eap_rx_failure ( netdev );
|
||||
return eap_rx_failure ( supplicant );
|
||||
default:
|
||||
DBGC ( netdev, "EAP %s unsupported code %d\n",
|
||||
netdev->name, eap->hdr.code );
|
||||
|
|
123
src/net/eapol.c
123
src/net/eapol.c
|
@ -28,7 +28,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
#include <byteswap.h>
|
||||
#include <ipxe/iobuf.h>
|
||||
#include <ipxe/if_ether.h>
|
||||
#include <ipxe/if_arp.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/vlan.h>
|
||||
#include <ipxe/eap.h>
|
||||
#include <ipxe/eapol.h>
|
||||
|
||||
|
@ -38,6 +40,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
*
|
||||
*/
|
||||
|
||||
struct net_driver eapol_driver __net_driver;
|
||||
|
||||
/** EAPoL destination MAC address */
|
||||
static const uint8_t eapol_mac[ETH_ALEN] = {
|
||||
0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
|
||||
};
|
||||
|
||||
/**
|
||||
* Process EAPoL packet
|
||||
*
|
||||
|
@ -51,12 +60,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev,
|
||||
const void *ll_dest __unused, const void *ll_source,
|
||||
unsigned int flags __unused ) {
|
||||
struct eapol_supplicant *supplicant;
|
||||
struct eapol_header *eapol;
|
||||
struct eapol_handler *handler;
|
||||
size_t remaining;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Find matching supplicant */
|
||||
supplicant = netdev_priv ( netdev, &eapol_driver );
|
||||
|
||||
/* Ignore non-EAPoL devices */
|
||||
if ( ! supplicant->eap.netdev ) {
|
||||
DBGC ( netdev, "EAPOL %s is not an EAPoL device\n",
|
||||
netdev->name );
|
||||
DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
|
||||
rc = -ENOTTY;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Sanity checks */
|
||||
if ( iob_len ( iobuf ) < sizeof ( *eapol ) ) {
|
||||
DBGC ( netdev, "EAPOL %s underlength header:\n",
|
||||
|
@ -83,7 +105,7 @@ static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev,
|
|||
/* Handle according to type */
|
||||
for_each_table_entry ( handler, EAPOL_HANDLERS ) {
|
||||
if ( handler->type == eapol->type ) {
|
||||
return handler->rx ( iob_disown ( iobuf ) , netdev,
|
||||
return handler->rx ( supplicant, iob_disown ( iobuf ),
|
||||
ll_source );
|
||||
}
|
||||
}
|
||||
|
@ -107,12 +129,14 @@ struct net_protocol eapol_protocol __net_protocol = {
|
|||
/**
|
||||
* Process EAPoL-encapsulated EAP packet
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v supplicant EAPoL supplicant
|
||||
* @v ll_source Link-layer source address
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev,
|
||||
static int eapol_eap_rx ( struct eapol_supplicant *supplicant,
|
||||
struct io_buffer *iobuf,
|
||||
const void *ll_source __unused ) {
|
||||
struct net_device *netdev = supplicant->eap.netdev;
|
||||
struct eapol_header *eapol;
|
||||
int rc;
|
||||
|
||||
|
@ -123,7 +147,8 @@ static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev,
|
|||
eapol = iob_pull ( iobuf, sizeof ( *eapol ) );
|
||||
|
||||
/* Process EAP packet */
|
||||
if ( ( rc = eap_rx ( netdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 ) {
|
||||
if ( ( rc = eap_rx ( &supplicant->eap, iobuf->data,
|
||||
iob_len ( iobuf ) ) ) != 0 ) {
|
||||
DBGC ( netdev, "EAPOL %s v%d EAP failed: %s\n",
|
||||
netdev->name, eapol->version, strerror ( rc ) );
|
||||
goto drop;
|
||||
|
@ -139,3 +164,93 @@ struct eapol_handler eapol_eap __eapol_handler = {
|
|||
.type = EAPOL_TYPE_EAP,
|
||||
.rx = eapol_eap_rx,
|
||||
};
|
||||
|
||||
/**
|
||||
* Transmit EAPoL packet
|
||||
*
|
||||
* @v supplicant EAPoL supplicant
|
||||
* @v type Packet type
|
||||
* @v data Packet body
|
||||
* @v len Length of packet body
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eapol_tx ( struct eapol_supplicant *supplicant, unsigned int type,
|
||||
const void *data, size_t len ) {
|
||||
struct net_device *netdev = supplicant->eap.netdev;
|
||||
struct io_buffer *iobuf;
|
||||
struct eapol_header *eapol;
|
||||
int rc;
|
||||
|
||||
/* Allocate I/O buffer */
|
||||
iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *eapol ) + len );
|
||||
if ( ! iobuf )
|
||||
return -ENOMEM;
|
||||
iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
|
||||
|
||||
/* Construct EAPoL header */
|
||||
eapol = iob_put ( iobuf, sizeof ( *eapol ) );
|
||||
eapol->version = EAPOL_VERSION_2001;
|
||||
eapol->type = type;
|
||||
eapol->len = htons ( len );
|
||||
|
||||
/* Append packet body */
|
||||
memcpy ( iob_put ( iobuf, len ), data, len );
|
||||
|
||||
/* Transmit packet */
|
||||
if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &eapol_protocol,
|
||||
&eapol_mac, netdev->ll_addr ) ) != 0 ) {
|
||||
DBGC ( netdev, "EAPOL %s could not transmit type %d: %s\n",
|
||||
netdev->name, type, strerror ( rc ) );
|
||||
DBGC_HDA ( netdev, 0, data, len );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit EAPoL-encapsulated EAP packet
|
||||
*
|
||||
* @v supplicant EAPoL supplicant
|
||||
* @v ll_source Link-layer source address
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data,
|
||||
size_t len ) {
|
||||
struct eapol_supplicant *supplicant =
|
||||
container_of ( eap, struct eapol_supplicant, eap );
|
||||
|
||||
/* Transmit encapsulated packet */
|
||||
return eapol_tx ( supplicant, EAPOL_TYPE_EAP, data, len );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create EAPoL supplicant
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v priv Private data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eapol_probe ( struct net_device *netdev, void *priv ) {
|
||||
struct eapol_supplicant *supplicant = priv;
|
||||
struct ll_protocol *ll_protocol = netdev->ll_protocol;
|
||||
|
||||
/* Ignore non-EAPoL devices */
|
||||
if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) )
|
||||
return 0;
|
||||
if ( vlan_tag ( netdev ) )
|
||||
return 0;
|
||||
|
||||
/* Initialise structure */
|
||||
supplicant->eap.netdev = netdev;
|
||||
supplicant->eap.tx = eapol_eap_tx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** EAPoL driver */
|
||||
struct net_driver eapol_driver __net_driver = {
|
||||
.name = "EAPoL",
|
||||
.priv_len = sizeof ( struct eapol_supplicant ),
|
||||
.probe = eapol_probe,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue