aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2023-09-15 16:10:07 +0100
committerMichael Brown <mcb30@ipxe.org>2023-09-18 12:07:28 +0100
commit56cc61a168820c7cbbe23418388129ec11699a8c (patch)
tree270bd1ea47e8e3235595e99500927803d5e29a2d
parentcac3a584dc8acea1522669f1ed16e0979fb92252 (diff)
downloadipxe-56cc61a168820c7cbbe23418388129ec11699a8c.zip
ipxe-56cc61a168820c7cbbe23418388129ec11699a8c.tar.gz
ipxe-56cc61a168820c7cbbe23418388129ec11699a8c.tar.bz2
[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>
-rw-r--r--src/include/ipxe/eap.h21
-rw-r--r--src/include/ipxe/eapol.h13
-rw-r--r--src/net/80211/wpa.c11
-rw-r--r--src/net/eap.c43
-rw-r--r--src/net/eapol.c123
5 files changed, 184 insertions, 27 deletions
diff --git a/src/include/ipxe/eap.h b/src/include/ipxe/eap.h
index 6fe7018..e5f6065 100644
--- a/src/include/ipxe/eap.h
+++ b/src/include/ipxe/eap.h
@@ -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 */
diff --git a/src/include/ipxe/eapol.h b/src/include/ipxe/eapol.h
index 952d6c7..f6009a2 100644
--- a/src/include/ipxe/eapol.h
+++ b/src/include/ipxe/eapol.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 */
diff --git a/src/net/80211/wpa.c b/src/net/80211/wpa.c
index 1484d0e..17c11b8 100644
--- a/src/net/80211/wpa.c
+++ b/src/net/80211/wpa.c
@@ -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;
diff --git a/src/net/eap.c b/src/net/eap.c
index 8d1d540..beaeb61 100644
--- a/src/net/eap.c
+++ b/src/net/eap.c
@@ -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 );
diff --git a/src/net/eapol.c b/src/net/eapol.c
index 3578f0e..172037c 100644
--- a/src/net/eapol.c
+++ b/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,
+};