/** * A client-side 802.1x implementation supporting EAP/TLS * * This code is released under both the GPL version 2 and BSD licenses. * Either license may be used. The respective licenses are found below. * * Copyright (C) 2002 Bryan D. Payne & Nick L. Petroni Jr. * All Rights Reserved * * --- GPL Version 2 License --- * 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 (at your option) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * --- BSD License --- * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * Maryland at College Park and its contributors. * - Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /******************************************************************* * EAP Function implementations for supplicant * * File: eap.c * * Authors: bdpayne@cs.umd.edu, npetroni@cs.umd.edu * * $Id: eap.c,v 1.20 2003/03/13 21:43:10 chessing Exp $ * $Date: 2003/03/13 21:43:10 $ * $Log: eap.c,v $ * Revision 1.20 2003/03/13 21:43:10 chessing * Fix for wired/wireless problem that only shows up on MacOS-X (for now). Additional documentation in the 1x.conf file. * * Revision 1.19 2003/03/11 19:30:54 chessing * Various code cleanups * * Revision 1.18 2003/03/10 23:35:44 chessing * Patches to try to get TLS working with RADIUS servers other than FreeRADIUS. * * Revision 1.17 2003/03/02 06:40:24 npetroni * -allow @ symbol in identities in config_lex.l * -added error checking in eap.c to test if there is a valid id before * building idrsp * * Revision 1.16 2003/02/27 22:38:43 chessing * More error checking. TLS failures are reported better now. * * Revision 1.15 2003/02/27 04:05:55 chessing * Added more error checking, fixed a few omissions that have not yet caused bugs. ;) * * Revision 1.14 2003/02/07 18:46:49 chessing * First import of MS-CHAPv2 code. Works, but still needs some work done on it. * * Revision 1.13 2003/01/24 20:48:16 chessing * Cleaned up framing code, TLS no longer sends a malformed frame. * * Revision 1.12 2003/01/15 21:34:26 galimorerpg * Linux OpenSSL/TLS bug fixes. * * Revision 1.11 2003/01/14 23:52:07 chessing * More work on the TLS code. It should be mostly stable now. There is a problem if get_pass("") in eaptls_auth_challenge is called twice. * * Revision 1.10 2003/01/14 19:12:21 chessing * TLS code now uses OpenSSL! Cleaned out some of the no longer needed stuff from the TLS code. Still needs more work on error checking. * * Revision 1.9 2003/01/09 23:09:22 galimorerpg * Logging Updates. * * Revision 1.8 2003/01/09 21:36:52 galimorerpg * Logging Updates. * * Revision 1.7 2003/01/09 20:50:15 chessing * Code cleanups. * * Revision 1.6 2003/01/09 20:17:25 galimorerpg * Logging Updates * * Revision 1.5 2003/01/08 20:38:35 chessing * Fix for buffer problem with TLS authentication on Linux * * Revision 1.4 2003/01/03 22:25:35 chessing * Turned off debugging code, (use the configure options to turn on the debugging) and added -w option to get around some problems with the Intel iANS drivers, and some wired/wireless issues with the MacOS-X wireless code. * * Revision 1.3 2003/01/02 19:29:37 chessing * Update to bring the xsupplicant code current with the development work. * * *******************************************************************/ /*** *** Specifics for EAPOL can be found in Section 7 of *** IEEE Draft P802.1X/D11, ***/ #include #include #include #include #include #include "eap.h" #include "auth_methods/tls/eaptls.h" #include "auth_methods/md5/eapmd5.h" #include "auth_methods/mschapv2/eapmschapv2.h" #include "frame_handlers/os_frame_funcs.h" #include "configparse.h" #include "dot1x_globals.h" #include "userconf.h" #include "logging.h" #ifndef EAP_DEBUG #define EAP_DEBUG 0 #endif /** GLOBAL VARS **/ int eap_auth_types[] = { EAP_TYPE_TLS, EAP_TYPE_MD5, EAP_TYPE_MSCHAP, NO_EAP_AUTH }; char *eap_auth_names[] = { "TLS", "MD5", "MSCHAPv2" }; int (*eap_auth_inits[])(char *, char *) = { init_eaptls, init_eapmd5, init_eapmschap }; int (*eap_auth_challenge[])() = { eaptls_auth_challenge, eapmd5_auth_challenge, eapmschap_auth_challenge }; int (*eap_auth_handlers[])(u_char *, int, u_char *, int *) = { eaptls_decode_packet, eapmd5_decode_packet, eapmschap_decode_packet }; int (*eap_auth_cleanup[])() = { eaptls_shutdown, eapmd5_shutdown, eapmschap_shutdown }; char *eap_config; char *eap_netid; /** FUNCTION DEFINITIONS **/ #if EAP_DEBUG /* THE DEBUG FUNCTIONS */ void eap_debug(char *fun, u_char *buf, int size, char *comment) { int i; xlogf(DEBUG_AUTHTYPES, "## %s ##: %s", fun, comment); if (buf != NULL && EAP_DEBUG >= 2) { xlogf(DEBUG_AUTHTYPES, "\nPacket is length: %x hex %d decimal", size, size); for (i = 0; i < size; i++) { if (i % 16 == 0) xlogf(DEBUG_AUTHTYPES, "\n%.3x\t", i); xlogf(DEBUG_AUTHTYPES, "%2x ", *(buf+i)); } } xlogf(DEBUG_AUTHTYPES, "\n"); } #endif int init_eap(char *config, char *netid) { int auth = 0; eap_config = config; eap_netid = netid; /* Initialize Auth types */ while (eap_auth_types[auth] != NO_EAP_AUTH) { if ((*eap_auth_inits[auth++])(config, netid) != 0) { xlogf(DEBUG_NORMAL, "Couldn't initalize eap type : %s\n", eap_auth_names[auth]); xlogf(DEBUG_NORMAL, "If you are trying to use this authentication method, it probably won't work!\n"); } } return 0; } //Check to see if we have a valid EAP type. (If so, return the auth index.) int is_valid_eap(char *in_eap) { int auth=0; while ((strncmp(eap_auth_names[auth], in_eap, strlen(in_eap) != 0)) && (eap_auth_types[auth] != NO_EAP_AUTH)) { // We don't have a match. Check the next one. auth++; } if (eap_auth_types[auth] != NO_EAP_AUTH) { return eap_auth_types[auth]; } return -1; // We didn't find a match. } // Return a list of known EAP types. char *return_known_eap_types() { int auth = 0; char *eap_types; int eap_size=0; int num_types=0; char *comma_space = ", "; char *period = "."; while (eap_auth_types[auth] != NO_EAP_AUTH) { eap_size+=strlen(eap_auth_names[auth]); eap_size+=2; // for a comma and space. auth++; num_types++; } auth = 0; eap_types = (char *)malloc(eap_size); // Build a string big enough. eap_types[0] = 0x00; // Set the first character to a NULL. while (eap_auth_types[auth] != NO_EAP_AUTH) { strcat(eap_types, eap_auth_names[auth]); if (auth < (num_types-1)) { strcat(eap_types, comma_space); } else { strcat(eap_types, period); } auth++; } return eap_types; } // Call the shutdown routines for all of the authentication types. int shutdown_eap() { int auth = 0; while (eap_auth_types[auth] != NO_EAP_AUTH) { (* eap_auth_cleanup[auth++])(); } return 0; } // Here, we want to actually build a responseId. int eap_build_responseId(u_char *out, int *out_size) { const int eap_header_size = 5; u_char *temp_buf = NULL; int temp_size; struct my_eapol_header *eapol_header = (struct my_eapol_header *)out; struct my_eapol_packet *eapol_packet = (struct my_eapol_packet *)out; /* XXX Really, I would like to see us reparse here for the right network, but for now I am just checking to see we dont' segfault */ temp_buf = get_username(); if (temp_buf == NULL) return -1; temp_size = strlen(temp_buf); // The username is appended to the frame. // So, we need to know how long it is. // Set up the EAP values needed for the frame. eapol_header->eapol_version = 1; eapol_header->eapol_type = EAPOL_PACKET; // The length value that is reported in the frame is the length of the EAP // portion only. So, the actual size of the frame will be 18 bytes larger. // 12 (src/dst MAC) + 2 (EAPOL Frame Type) + 4 (EAPOL Header) eapol_header->eapol_length = htons(eap_header_size+temp_size); eapol_packet->code = EAP_RESPONSE; eapol_packet->identifier = get_receivedId(); eapol_packet->length = eapol_header->eapol_length; eapol_packet->type = EAP_TYPE_ID; // At offset 23 in to the frame, we should have non-header data, such as // the username. Copy the username in to the frame. memcpy(&out[EAP_PAYLOAD_OFFSET], temp_buf, temp_size); // out_size should be the size of the data we created here. Ignore the // additional MAC header bytes, as they will be added by the calling function *out_size = EAP_PAYLOAD_OFFSET+temp_size; if (temp_buf != NULL) free(temp_buf); temp_buf = NULL; return 0; } // Build the frame that will carry our authentication response. // int eap_build_auth_response(u_char *in, int in_size, u_char *out, int *out_size) { const int eapol_header_size = 5; int auth=0; u_char *temp_buf=NULL; u_char *payload = in + 9; // This is the first byte in the payload. struct my_eapol_header *eapol_header = (struct my_eapol_header *)out; struct my_eapol_packet *eapol_packet = (struct my_eapol_packet *)out; // This is the offset to get past the MAC header. int payload_size = in_size - 9; // This is the payload size, minus all headers. int temp_size; // First, see if we have a handler for this case. while ((eap_auth_types[auth] != get_authType()) && (eap_auth_types[auth] != NO_EAP_AUTH)) auth++; if (eap_auth_types[auth] == NO_EAP_AUTH) { xlogf(DEBUG_NORMAL, "No authentication handler for requested EAP type (%x)\n",get_authType()); return -1; } // Now, determine if this is our first authentication, or if we are // reauthenticating. if (get_suppStatus() != AUTHORIZED) { // We need to get a password, or some other authentication information. if ((eap_auth_challenge[auth])() < 0) { xlogf(DEBUG_NORMAL, "There was an error getting the authentication challenge information.\n"); return -1; } } // Now, build the authentication answer. (Allocate 1500 bytes, since our // ethernet MTU is 1514, but we aren't dealing with the 14 bytes for header.) temp_buf = (char *)malloc(1500); bzero(temp_buf, 1500); #if EAP_DEBUG xlogf(DEBUG_EVERYTHING, "payload size = %d in_size = %d\n",payload_size,in_size); #endif if ((* eap_auth_handlers[auth])(payload, payload_size, temp_buf, &temp_size) == -1) { #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "handler returned -1"); #endif return -1; } #if EAP_DEBUG xlogf(DEBUG_EVERYTHING, "Destination : %x:%x:%x:%x:%x:%x\n", out[0], out[1], out[2], out[3], out[4], out[5]); #endif eapol_header->eapol_version = 1; eapol_header->eapol_type = EAPOL_PACKET; eapol_header->eapol_length = htons(eapol_header_size+temp_size); //Make sure we have the correct byte order. eapol_packet->code = EAP_RESPONSE; eapol_packet->identifier = get_receivedId(); eapol_packet->length = eapol_header->eapol_length; eapol_packet->type = eap_auth_types[auth]; memcpy(&out[EAP_PAYLOAD_OFFSET], temp_buf, temp_size); free(temp_buf); //We no longer need temp_buf *out_size = EAP_PAYLOAD_OFFSET+temp_size; return 0; } int eap_decode_packet(u_char *in) { struct my_eapol_packet * mypacket = (struct my_eapol_packet *)(in); int retVal = 0; /* USE #defines here because the struct is not the same size as the actual header (padding) */ /* decode based on the EAP code */ switch(mypacket->code) { case EAP_REQUEST: #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "Got an EAP request"); #endif switch(mypacket->type) { case EAP_TYPE_ID: #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "Type is Identity"); #endif retVal = 1; break; case EAP_TYPE_NOTIFY: xlogf(DEBUG_NORMAL, "Received EAP Notify\n"); break; case EAP_TYPE_NAK: xlogf(DEBUG_NORMAL, "Received EAP NAK\n"); break; default: set_authType(mypacket->type); retVal = 0; } break; case EAP_RESPONSE: #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "Got an EAP response"); #endif xlogf(DEBUG_NORMAL, "received EAP_RESPONSE packet -- error\n"); retVal = 0; // We should return an error here, but if we do the current code will cause the supplicant to die. break; case EAP_SUCCESS: #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "Got an EAP success"); #endif retVal = 3; break; case EAP_FAILURE: #if EAP_DEBUG eap_debug("eap_decode_packet", NULL, 0, "Got an EAP failure"); #endif retVal = 4; break; default: xlogf(DEBUG_NORMAL, "Unknown EAP code type\n"); } return retVal; } /*** EOF ***/