diff options
author | Michael Brown <mcb30@ipxe.org> | 2024-01-10 15:30:36 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2024-01-10 16:03:10 +0000 |
commit | c6226f104e22db7d19b4c983e962d84b5665c04b (patch) | |
tree | 314faf14e92db9dceb635d8e1a725fc1cf00d45c | |
parent | 0abb3e85e518c4d9366d4555093c0aff0c060858 (diff) | |
download | ipxe-c6226f104e22db7d19b4c983e962d84b5665c04b.tar.gz |
[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>
-rw-r--r-- | src/include/ipxe/eap.h | 45 | ||||
-rw-r--r-- | src/net/eap.c | 157 |
2 files changed, 177 insertions, 25 deletions
diff --git a/src/include/ipxe/eap.h b/src/include/ipxe/eap.h index 4b689cc24..bbae517da 100644 --- a/src/include/ipxe/eap.h +++ b/src/include/ipxe/eap.h @@ -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 ); diff --git a/src/net/eap.c b/src/net/eap.c index 8ba87e292..fe01f136e 100644 --- a/src/net/eap.c +++ b/src/net/eap.c @@ -23,7 +23,10 @@ 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> @@ -34,59 +37,173 @@ 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; |