diff options
author | Simon Rettberg | 2024-04-12 14:00:15 +0200 |
---|---|---|
committer | Simon Rettberg | 2024-04-12 14:00:15 +0200 |
commit | 98dc341428e247141f120d05fac48c4e144a4c0f (patch) | |
tree | 3ebacb37927e338383ac64c2e20eb0b2f820cb85 /src/net/eap_mschapv2.c | |
parent | Merge branch 'master' into openslx (diff) | |
parent | Merge branch 'ipxe:master' into aqc1xx (diff) | |
download | ipxe-98dc341428e247141f120d05fac48c4e144a4c0f.tar.gz ipxe-98dc341428e247141f120d05fac48c4e144a4c0f.tar.xz ipxe-98dc341428e247141f120d05fac48c4e144a4c0f.zip |
Merge branch 'aqc1xx' into openslx
Diffstat (limited to 'src/net/eap_mschapv2.c')
-rw-r--r-- | src/net/eap_mschapv2.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/src/net/eap_mschapv2.c b/src/net/eap_mschapv2.c new file mode 100644 index 00000000..0be62ed5 --- /dev/null +++ b/src/net/eap_mschapv2.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/mschapv2.h> +#include <ipxe/eap.h> + +/** @file + * + * EAP MS-CHAPv2 authentication method + * + * EAP-MSCHAPv2 was described in a draft RFC first published in 2002 + * (draft-kamath-pppext-eap-mschapv2-02.txt). The draft eventually + * expired in 2007 without becoming an official RFC, quite possibly + * because the protocol design was too ugly to be called an IETF + * standard. It is, however, fairly widely used. + */ + +/** An EAP MS-CHAPv2 request message */ +struct eap_mschapv2_request { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** MS-CHAPv2 challenge length (fixed value) */ + uint8_t len; + /** MS-CHAPv2 challenge */ + struct mschapv2_challenge msg; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 response message */ +struct eap_mschapv2_response { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** MS-CHAPv2 response length (fixed value) */ + uint8_t len; + /** MS-CHAPv2 response */ + struct mschapv2_response msg; + /** User name */ + char name[0]; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 success request message */ +struct eap_mschapv2_success_request { + /** EAP-MSCHAPv2 header */ + struct eap_mschapv2 hdr; + /** Message */ + char message[0]; +} __attribute__ (( packed )); + +/** An EAP MS-CHAPv2 success response message */ +struct eap_mschapv2_success_response { + /** Opcode */ + uint8_t code; +} __attribute__ (( packed )); + +/** + * Handle EAP MS-CHAPv2 request + * + * @v supplicant EAP supplicant + * @v hdr EAP-MSCHAPv2 header + * @v len Message length + * @ret rc Return status code + */ +static int eap_rx_mschapv2_request ( struct eap_supplicant *supplicant, + const struct eap_mschapv2 *hdr, + size_t len ) { + struct net_device *netdev = supplicant->netdev; + struct settings *settings = netdev_settings ( netdev ); + const struct eap_mschapv2_request *msreq = + container_of ( hdr, struct eap_mschapv2_request, hdr ); + struct eap_mschapv2_response *msrsp; + struct mschapv2_challenge peer; + char *username; + char *password; + int username_len; + int password_len; + size_t msrsp_len; + unsigned int i; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msreq ) ) { + DBGC ( netdev, "EAP %s underlength MS-CHAPv2 request\n", + netdev->name ); + DBGC_HDA ( netdev, 0, hdr, len ); + rc = -EINVAL; + goto err_sanity; + } + + /* Fetch username and password */ + username_len = fetch_string_setting_copy ( settings, &username_setting, + &username ); + if ( username_len < 0 ) { + rc = username_len; + DBGC ( netdev, "EAP %s has no username: %s\n", + netdev->name, strerror ( rc ) ); + goto err_username; + } + password_len = fetch_string_setting_copy ( settings, &password_setting, + &password ); + if ( password_len < 0 ) { + rc = password_len; + DBGC ( netdev, "EAP %s has no password: %s\n", + netdev->name, strerror ( rc ) ); + goto err_password; + } + + /* Construct a peer challenge. We do not perform mutual + * authentication, so this does not need to be strong. + */ + for ( i = 0 ; i < ( sizeof ( peer.byte ) / + sizeof ( peer.byte[0] ) ) ; i++ ) { + peer.byte[i] = random(); + } + + /* Allocate response */ + msrsp_len = ( sizeof ( *msrsp ) + username_len ); + msrsp = malloc ( msrsp_len ); + if ( ! msrsp ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct response */ + msrsp->hdr.code = EAP_CODE_RESPONSE; + msrsp->hdr.id = msreq->hdr.id; + msrsp->hdr.len = htons ( msrsp_len ); + msrsp->len = sizeof ( msrsp->msg ); + mschapv2_response ( username, password, &msreq->msg, &peer, + &msrsp->msg ); + memcpy ( msrsp->name, username, username_len ); + + /* Send response */ + if ( ( rc = eap_tx_response ( supplicant, msrsp, msrsp_len ) ) != 0 ) + goto err_tx; + + err_tx: + free ( msrsp ); + err_alloc: + free ( password ); + err_password: + free ( username ); + err_username: + err_sanity: + return rc; +} + +/** + * Handle EAP MS-CHAPv2 success request + * + * @v supplicant EAP supplicant + * @v hdr EAP-MSCHAPv2 header + * @v len Message length + * @ret rc Return status code + */ +static int eap_rx_mschapv2_success ( struct eap_supplicant *supplicant, + const struct eap_mschapv2 *hdr, + size_t len ) { + const struct eap_mschapv2_success_request *msreq = + container_of ( hdr, struct eap_mschapv2_success_request, hdr ); + static const struct eap_mschapv2_success_response msrsp = { + .code = EAP_CODE_SUCCESS, + }; + + /* Sanity check */ + assert ( len >= sizeof ( *msreq ) ); + + /* The success request contains the MS-CHAPv2 authenticator + * response, which could potentially be used to verify that + * the EAP authenticator also knew the password (or, at least, + * the MD4 hash of the password). + * + * Our model for EAP does not encompass mutual authentication: + * we will starting sending plaintext packets (e.g. DHCP + * requests) over the link even before EAP completes, and our + * only use for an EAP success is to mark the link as + * unblocked. + * + * We therefore ignore the content of the success request and + * just send back a success response, so that the EAP + * authenticator will complete the process and send through + * the real EAP success packet (which will, in turn, cause us + * to unblock the link). + */ + return eap_tx_response ( supplicant, &msrsp, sizeof ( msrsp ) ); +} + +/** + * Handle EAP MS-CHAPv2 + * + * @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_mschapv2 ( struct eap_supplicant *supplicant, + const void *req, size_t req_len ) { + struct net_device *netdev = supplicant->netdev; + const struct eap_mschapv2 *hdr = req; + + /* Sanity check */ + if ( req_len < sizeof ( *hdr ) ) { + DBGC ( netdev, "EAP %s underlength MS-CHAPv2:\n", + netdev->name ); + DBGC_HDA ( netdev, 0, req, req_len ); + return -EINVAL; + } + + /* Handle according to opcode */ + switch ( hdr->code ) { + case EAP_CODE_REQUEST: + return eap_rx_mschapv2_request ( supplicant, hdr, req_len ); + case EAP_CODE_SUCCESS: + return eap_rx_mschapv2_success ( supplicant, hdr, req_len ); + default: + DBGC ( netdev, "EAP %s unsupported MS-CHAPv2 opcode %d\n", + netdev->name, hdr->code ); + DBGC_HDA ( netdev, 0, req, req_len ); + return -ENOTSUP; + } +} + +/** EAP MS-CHAPv2 method */ +struct eap_method eap_mschapv2_method __eap_method = { + .type = EAP_TYPE_MSCHAPV2, + .rx = eap_rx_mschapv2, +}; |