]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[eap] Define a supplicant model for EAP and EAPoL
authorMichael Brown <mcb30@ipxe.org>
Fri, 15 Sep 2023 15:10:07 +0000 (16:10 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 18 Sep 2023 11:07:28 +0000 (12:07 +0100)
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>
src/include/ipxe/eap.h
src/include/ipxe/eapol.h
src/net/80211/wpa.c
src/net/eap.c
src/net/eapol.c

index 6fe70189b9c99adb574d360eea57827c58e85047..e5f6065539cf4dfeaed1a51605f5ddd9c5ce9bd2 100644 (file)
@@ -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 */
index 952d6c75206f15b93a23812061f4e473f4b8ae54..f6009a2ff504cfa163dd57e78cc1f3da6f9afa84 100644 (file)
@@ -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 */
index 1484d0e80ded9e4909b787ddae179a19552096ad..17c11b8ed8ce4dbf88039c52cc5295fdfaf5f268 100644 (file)
@@ -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;
index 8d1d540fb8654c586152216a858dec07b324ad31..beaeb61d2bad45847f6577534ee1ff193dfe3aa6 100644 (file)
@@ -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 );
index 3578f0e37a0f44febff7efab13194c792c880fc9..172037ce1e713a9e6171391cd1776bdf07965d58 100644 (file)
@@ -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,
+};