/* * Copyright (C) 2024 Michael Brown . * * 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 #include #include #include #include #include /** @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, };