Compare commits

...

3 Commits

Author SHA1 Message Date
ff0f860483 [libc] Use wall clock time as seed for the (non-cryptographic) RNG
We currently use the number of timer ticks since power-on as a seed
for the non-cryptographic RNG implemented by random().  Since iPXE is
often executed directly after power-on, and since the timer tick
resolution is generally low, this can often result in identical seed
values being used on each cold boot attempt.

As of commit 41f786c ("[settings] Add "unixtime" builtin setting to
expose the current time"), the current wall-clock time is always
available within the default build of iPXE.  Use this time instead, to
introduce variability between cold boot attempts on the same host.
(Note that variability between different hosts is obtained by using
the MAC address as an additional seed value.)

This has no effect on the separate DRBG used by cryptographic code.

Suggested-by: Heiko <heik0@xs4all.nl>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-10-06 12:50:43 +01:00
8b14652e50 [eapol] Send EAPoL-Start packets to trigger EAP authentication
We have no way to force a link-layer restart in iPXE, and therefore no
way to explicitly trigger a restart of EAP authentication.  If an iPXE
script has performed some action that requires such a restart
(e.g. registering a device such that the port VLAN assignment will be
changed), then the only means currently available to effect the
restart is to reboot the whole system.  If iPXE is taking over a
physical link already used by a preceding bootloader, then even a
reboot may not work.

In the EAP model, the supplicant is a pure responder and never
initiates transmissions.  EAPoL extends this to include an EAPoL-Start
packet type that may be sent by the supplicant to (re)trigger EAP.

Add support for sending EAPoL-Start packets at two-second intervals on
links that are open and have reached physical link-up, but for which
EAP has not yet completed.  This allows "ifclose ; ifopen" to be used
to restart the EAP process.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-09-19 23:16:58 +01:00
56cc61a168 [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>
2023-09-18 12:07:28 +01:00
6 changed files with 270 additions and 30 deletions

View File

@ -6,8 +6,9 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdlib.h>
#include <ipxe/timer.h>
#include <time.h>
static int32_t rnd_seed = 0;
@ -30,8 +31,9 @@ void srandom ( unsigned int seed ) {
long int random ( void ) {
int32_t q;
if ( ! rnd_seed ) /* Initialize linear congruential generator */
srandom ( currticks() );
/* Initialize linear congruential generator */
if ( ! rnd_seed )
srandom ( time ( NULL ) );
/* simplified version of the LCG given in Bruce Schneier's
"Applied Cryptography" */

View 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 */

View 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 {
@ -29,9 +30,23 @@ struct eapol_header {
/** EAPoL-encapsulated EAP packets */
#define EAPOL_TYPE_EAP 0
/** EAPoL start */
#define EAPOL_TYPE_START 1
/** EAPoL key */
#define EAPOL_TYPE_KEY 5
/** An EAPoL supplicant */
struct eapol_supplicant {
/** EAP supplicant */
struct eap_supplicant eap;
/** EAPoL-Start retransmission timer */
struct retry_timer timer;
};
/** Delay between EAPoL-Start packets */
#define EAPOL_START_INTERVAL ( 2 * TICKS_PER_SEC )
/** An EAPoL handler */
struct eapol_handler {
/** Type */
@ -39,15 +54,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 */

View 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;

View 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 );

View File

@ -28,7 +28,10 @@ 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/retry.h>
#include <ipxe/eap.h>
#include <ipxe/eapol.h>
@ -38,6 +41,44 @@ 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
};
/**
* Update EAPoL supplicant state
*
* @v supplicant EAPoL supplicant
* @v timeout Timer ticks until next EAPoL-Start (if applicable)
*/
static void eapol_update ( struct eapol_supplicant *supplicant,
unsigned long timeout ) {
struct net_device *netdev = supplicant->eap.netdev;
/* Check device and EAP state */
if ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) {
if ( supplicant->eap.done ) {
/* EAP has completed: stop sending EAPoL-Start */
stop_timer ( &supplicant->timer );
} else if ( ! timer_running ( &supplicant->timer ) ) {
/* EAP has not yet begun: start sending EAPoL-Start */
start_timer_fixed ( &supplicant->timer, timeout );
}
} else {
/* Not ready: clear completion and stop sending EAPoL-Start */
supplicant->eap.done = 0;
stop_timer ( &supplicant->timer );
}
}
/**
* Process EAPoL packet
*
@ -51,12 +92,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 +137,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 +161,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,12 +179,16 @@ 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;
}
/* Update supplicant state */
eapol_update ( supplicant, EAPOL_START_INTERVAL );
drop:
free_iob ( iobuf );
return rc;
@ -139,3 +199,131 @@ 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 );
}
/**
* (Re)transmit EAPoL-Start packet
*
* @v timer EAPoL-Start timer
* @v expired Failure indicator
*/
static void eapol_expired ( struct retry_timer *timer, int fail __unused ) {
struct eapol_supplicant *supplicant =
container_of ( timer, struct eapol_supplicant, timer );
struct net_device *netdev = supplicant->eap.netdev;
/* Schedule next transmission */
start_timer_fixed ( timer, EAPOL_START_INTERVAL );
/* Transmit EAPoL-Start, ignoring errors */
DBGC2 ( netdev, "EAPOL %s transmitting Start\n", netdev->name );
eapol_tx ( supplicant, EAPOL_TYPE_START, NULL, 0 );
}
/**
* 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;
timer_init ( &supplicant->timer, eapol_expired, &netdev->refcnt );
return 0;
}
/**
* Handle EAPoL supplicant state change
*
* @v netdev Network device
* @v priv Private data
*/
static void eapol_notify ( struct net_device *netdev __unused, void *priv ) {
struct eapol_supplicant *supplicant = priv;
/* Ignore non-EAPoL devices */
if ( ! supplicant->eap.netdev )
return;
/* Update supplicant state */
eapol_update ( supplicant, 0 );
}
/** EAPoL driver */
struct net_driver eapol_driver __net_driver = {
.name = "EAPoL",
.priv_len = sizeof ( struct eapol_supplicant ),
.probe = eapol_probe,
.notify = eapol_notify,
};