| /* | |
| * 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 ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #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, | |
| }; |