mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ae26de3ec | |||
| 6db299dc0f | |||
| dc15c6e633 |
@ -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 {
|
||||
@ -26,17 +27,39 @@ struct eap_header {
|
||||
/** EAP request */
|
||||
#define EAP_CODE_REQUEST 1
|
||||
|
||||
/** EAP request */
|
||||
struct eap_request {
|
||||
/** EAP response */
|
||||
#define EAP_CODE_RESPONSE 2
|
||||
|
||||
/** 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 MD5 challenge request/response */
|
||||
#define EAP_TYPE_MD5 4
|
||||
|
||||
/** EAP MD5 challenge request/response type data */
|
||||
struct eap_md5 {
|
||||
/** Value length */
|
||||
uint8_t len;
|
||||
/** Value */
|
||||
uint8_t value[0];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** EAP success */
|
||||
#define EAP_CODE_SUCCESS 3
|
||||
|
||||
@ -47,8 +70,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
|
||||
@ -87,7 +110,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
|
||||
*
|
||||
@ -117,6 +144,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 );
|
||||
|
||||
|
||||
240
src/net/eap.c
240
src/net/eap.c
@ -23,8 +23,13 @@
|
||||
|
||||
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/md5.h>
|
||||
#include <ipxe/chap.h>
|
||||
#include <ipxe/eap.h>
|
||||
|
||||
/** @file
|
||||
@ -34,59 +39,251 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle EAP Request-Identity
|
||||
* 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_rx_request_identity ( struct eap_supplicant *supplicant ) {
|
||||
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_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).
|
||||
/* 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;
|
||||
|
||||
return 0;
|
||||
rc = 0;
|
||||
goto no_response;
|
||||
}
|
||||
|
||||
/* 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 Challenge-MD5
|
||||
*
|
||||
* @v req Request type data
|
||||
* @v req_len Length of request type data
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int eap_rx_md5 ( struct eap_supplicant *supplicant,
|
||||
const void *req, size_t req_len ) {
|
||||
struct net_device *netdev = supplicant->netdev;
|
||||
const struct eap_md5 *md5req = req;
|
||||
struct {
|
||||
uint8_t len;
|
||||
uint8_t value[MD5_DIGEST_SIZE];
|
||||
} __attribute__ (( packed )) md5rsp;
|
||||
struct chap_response chap;
|
||||
void *secret;
|
||||
int secret_len;
|
||||
int rc;
|
||||
|
||||
/* Sanity checks */
|
||||
if ( req_len < sizeof ( *md5req ) ) {
|
||||
DBGC ( netdev, "EAP %s underlength Challenge-MD5:\n",
|
||||
netdev->name );
|
||||
DBGC_HDA ( netdev, 0, req, req_len );
|
||||
rc = -EINVAL;
|
||||
goto err_sanity;
|
||||
}
|
||||
if ( ( req_len - sizeof ( *md5req ) ) < md5req->len ) {
|
||||
DBGC ( netdev, "EAP %s truncated Challenge-MD5:\n",
|
||||
netdev->name );
|
||||
DBGC_HDA ( netdev, 0, req, req_len );
|
||||
rc = -EINVAL;
|
||||
goto err_sanity;
|
||||
}
|
||||
|
||||
/* Construct response */
|
||||
if ( ( rc = chap_init ( &chap, &md5_algorithm ) ) != 0 ) {
|
||||
DBGC ( netdev, "EAP %s could not initialise CHAP: %s\n",
|
||||
netdev->name, strerror ( rc ) );
|
||||
goto err_chap;
|
||||
}
|
||||
chap_set_identifier ( &chap, supplicant->id );
|
||||
secret_len = fetch_raw_setting_copy ( netdev_settings ( netdev ),
|
||||
&password_setting, &secret );
|
||||
if ( secret_len < 0 ) {
|
||||
rc = secret_len;
|
||||
DBGC ( netdev, "EAP %s has no secret: %s\n",
|
||||
netdev->name, strerror ( rc ) );
|
||||
goto err_secret;
|
||||
}
|
||||
chap_update ( &chap, secret, secret_len );
|
||||
chap_update ( &chap, md5req->value, md5req->len );
|
||||
chap_respond ( &chap );
|
||||
assert ( chap.response_len == sizeof ( md5rsp.value ) );
|
||||
md5rsp.len = sizeof ( md5rsp.value );
|
||||
memcpy ( md5rsp.value, chap.response, sizeof ( md5rsp.value ) );
|
||||
|
||||
/* Transmit response */
|
||||
if ( ( rc = eap_tx_response ( supplicant, &md5rsp,
|
||||
sizeof ( md5rsp ) ) ) != 0 )
|
||||
goto err_tx;
|
||||
|
||||
err_tx:
|
||||
free ( secret );
|
||||
err_secret:
|
||||
chap_finish ( &chap );
|
||||
err_chap:
|
||||
err_sanity:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** EAP Challenge-MD5 method */
|
||||
struct eap_method eap_md5_method __eap_method = {
|
||||
.type = EAP_TYPE_MD5,
|
||||
.rx = eap_rx_md5,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 +345,10 @@ 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;
|
||||
case EAP_CODE_SUCCESS:
|
||||
return eap_rx_success ( supplicant );
|
||||
case EAP_CODE_FAILURE:
|
||||
|
||||
Reference in New Issue
Block a user