]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[eap] Add support for sending an EAP identity
authorMichael Brown <mcb30@ipxe.org>
Wed, 10 Jan 2024 15:30:36 +0000 (15:30 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 10 Jan 2024 16:03:10 +0000 (16:03 +0000)
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>
src/include/ipxe/eap.h
src/net/eap.c

index 4b689cc24df9d2f961b5cae1d4101a237a001dcf..bbae517da4a8b10116327ab90f4163fbc9117ab6 100644 (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 {
@@ -29,17 +30,25 @@ struct eap_header {
 /** EAP response */
 #define EAP_CODE_RESPONSE 2
 
-/** EAP request */
-struct eap_request {
+/** 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 success */
 #define EAP_CODE_SUCCESS 3
 
@@ -50,8 +59,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
@@ -90,7 +99,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
         *
@@ -120,6 +133,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 );
 
index 8ba87e292291fdae1f3fb7e93c0ca216b7bb21f5..fe01f136ef034ac2048e581e0b28e36325529b9b 100644 (file)
 
 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/eap.h>
 
@@ -33,60 +36,174 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
+/**
+ * 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_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_request_identity ( struct eap_supplicant *supplicant ) {
+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 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 +265,7 @@ 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;