/** * 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. */ /******************************************************************* * EAPOL Function implementations for supplicant * * File: eapol.c * * Authors: bdpayne@cs.umd.edu, npetroni@cs.umd.edu * * $Id: eapol.c,v 1.32 2003/03/13 22:31:11 chessing Exp $ * $Date: 2003/03/13 22:31:11 $ * $Log: eapol.c,v $ * Revision 1.32 2003/03/13 22:31:11 chessing * Another attempt at the wired/wireless fix. * * Revision 1.31 2003/03/13 21:43:11 chessing * Fix for wired/wireless problem that only shows up on MacOS-X (for now). Additional documentation in the 1x.conf file. * * Revision 1.30 2003/03/12 00:14:50 galimorerpg * Remove my previous fix; it broke wireless. * * Revision 1.29 2003/03/11 23:10:54 galimorerpg * Fixed a bug where xsupplicant would quit on machines with no wireless card. * * Revision 1.28 2003/03/11 19:30:54 chessing * Various code cleanups * * Revision 1.27 2003/03/05 21:18:41 npetroni * migration to libdnet. Please notify developers of build problems AFTER doing a cvs update. * * Revision 1.26 2003/03/04 19:33:41 npetroni * modified xsupp to correctly fork and exec a process after auth and then kill it * when quitting * * Revision 1.25 2003/03/04 06:10:08 npetroni * changes to allow a program to be run after successful authentication * * Revision 1.24 2003/03/03 23:28:59 npetroni * more fixing/testing/cleaning up key stuff * * Revision 1.23 2003/03/03 03:30:42 npetroni * rekeying works. Added a lot of stuff back to eapcrypt that had been removed * fixed process_keys * * Revision 1.22 2003/03/02 20:13:05 npetroni * increased error checking for a number of function calls * fixed tls to set ctx = NULL after freeing it (redundant free's segfault) * * Revision 1.21 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.20 2003/02/27 22:38:43 chessing * More error checking. TLS failures are reported better now. * * Revision 1.19 2003/02/27 04:05:56 chessing * Added more error checking, fixed a few omissions that have not yet caused bugs. ;) * * Revision 1.18 2003/02/17 19:32:47 chessing * Added the -m (NAK) option support by default, fixed a few cosmetic bugs in the MD5 code, and fixed build process with the new 1xdaemon.c code. * * Revision 1.17 2003/02/10 01:57:54 galimorerpg * Double free fix. * Code cleanups. * * Revision 1.16 2003/02/07 22:17:29 galimorerpg * Socket code to allow for daemonizing has been added. * * Revision 1.15 2003/02/04 20:17:39 galimorerpg * 02/04/03: * Modifications to allow daemonization have been implemented: * 1xdriver.c now sets up a server socket to listen for a front end connection for clients wishing to control the authentication process. * eapol.c: Fixed a bug in eapol_authenticate() that caused the main event loop in 1xdriver.c to stop functioning until the next auth cycle. * ChangeLog Updates. * * Revision 1.14 2003/02/03 17:46:20 galimorerpg * * CV: segfault dealing with eapol_handle_logoff() * Fixed a problem where eapol_handle_logoff() wasn't getting called correctly on exit. * State machine cleanups: * Moved all direct uses to eapol_current_state to function calls. * HELD state should now work properly. * LOGOFF state fixes. * * Revision 1.13 2003/02/02 03:29:02 chessing * Fixed -a option, and updated MacOS-X wireless calls to return the MAC of the AP we are associated to. * * Revision 1.12 2003/01/24 20:48:16 chessing * Cleaned up framing code, TLS no longer sends a malformed frame. * * Revision 1.11 2003/01/09 23:09:22 galimorerpg * Logging Updates. * * Revision 1.10 2003/01/09 21:36:52 galimorerpg * Logging Updates. * * Revision 1.9 2003/01/09 20:50:15 chessing * Code cleanups. * * Revision 1.8 2003/01/09 20:17:26 galimorerpg * Logging Updates * * Revision 1.7 2003/01/09 17:39:30 chessing * Cleaned up wireless interfaces, removed some commented out code that isn't needed anymore. * * Revision 1.6 2003/01/06 23:00:54 chessing * * Documentation updates, and supplicant no longer terminates when it sees EAPOL-Start messages from other clients. * * Revision 1.5 2003/01/06 22:31:04 chessing * Debugging code cleanups.... * * 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 #include #include #include "eapol.h" #include "configparse.h" #include "auth_methods/tls/eapcrypt.h" #include "frame_handlers/os_frame_funcs.h" #include "dot1x_globals.h" #include "logging.h" #include "wireless/os_wireless_ext.h" #include "userconf.h" #ifndef EAPOL_DEBUG #define EAPOL_DEBUG 0 #endif #define NAK_SUPPORT // FreeRadius doesn't support this! /** GLOBAL VARS **/ // moved to 802.1x spec. globals to dot1x_globals.h #if EAPOL_DEBUG unsigned int successCount = 0; unsigned int failCount = 0; #endif /* IEEE 802.1x default recommendations */ /* Note that these don't need to be strict consts */ /* IEEE 802.1x specifies that the state machine shouldn't */ /* modify these values, so in that way, they are const, but not otherwise */ const int authPeriod = 30; const int heldPeriod = 60; const int startPeriod = 30; const int maxStart = 3; /* Globals for this specific implementation */ u_char *eapol_src = NULL; /* MAC address of interface */ u_char eapol_dst[ETH_ADDR_LEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03}; /* MAC address of authenticator */ u_char * eapol_device = NULL; /* Interface name */ u_char * eapol_config = "/etc/1x/1x.conf"; /* Configuration File */ char eapol_ssid[100] = ""; /* network name */ int xsupplicant_first_auth = 0; int xsupplicant_run_auth = 0; int xsupplicant_auth_command_pid = 0; // pid could never be 0 extern int errno; FILE *xsupplicant_run_command = NULL; // These are used in the key update routine, and should probably be changed. EapolStates eapol_current_state = DISCONNECTED; EapolStates eapol_last_state = DISCONNECTED; KeyReceiveStates eapol_key_receive_state; unsigned char is_wired = FALSE; #if EAPOL_DEBUG int getFailCount() { return failCount; } int getSuccessCount() { return successCount; } #endif /**** APPLICATION LAYER API *****/ /* THE DEBUG FUNCTION */ #if EAPOL_DEBUG void eapol_debug(char *fun, u_char *buf, int size, char *comment) { int i; xlogf(DEBUG_AUTHTYPES, "$$ %s $$: %s", fun, comment); if (buf != NULL && EAPOL_DEBUG >=2) { xlogf(DEBUG_AUTHTYPES, "\nPacket is %d bytes %x hex", size, size); for (i = 0; i < size; i++) { if (i % 16 == 0) xlogf(DEBUG_NORMAL, "\n\t"); xlogf(DEBUG_AUTHTYPES, "%2x ", *(buf+i)); } } xlogf(DEBUG_AUTHTYPES, "\n"); } #endif // This function catches a timer tick, and sets tick to true. Section 8.5.3.1.1 void alarm_tick() { set_tick(TRUE); alarm(1); //Call us again in 1 second. } int init_eapol(char *device, char *netid, u_char *auth_addr, char *config) { int retVal = 0; /* default to success */ char *reterr; // Pointer to hold an error return code. char *temp_char; // char *interface_type = get_client_type(); char *wired_type = "WIRED"; // Initalize the frame functions. reterr = initalize_frame_funcs(device, 1700, 0); if (reterr != NULL) { xlogf(DEBUG_NORMAL, "Error : %s\n", reterr); return -1; } eapol_device = get_dev_name(); if (eapol_device == NULL) return -1; //If we can't get an address name, die xlogf(DEBUG_NORMAL, "Setup on device %s complete\n", eapol_device); // Init wireless calls (if needed) if (eapol_wireless_init() < 0) { xlogf(DEBUG_NORMAL, "Couldn't initalize wireless extensions, assuming port is wired!\n"); set_client_type(wired_type); } /* setup config file */ if (config != NULL) { eapol_config = config; } /* set authenticator address */ if (auth_addr != NULL) { #if EAPOL_DEBUG xlogf(DEBUG_NORMAL, "Setting destination to : %x:%x:%x:%x:%x:%x\n", auth_addr[0], auth_addr[1], auth_addr[2], auth_addr[3], auth_addr[4], auth_addr[5]); #endif memcpy(eapol_dst, auth_addr, ETH_ADDR_LEN); } else { temp_char = get_client_type(); if (temp_char == NULL || (strncmp(temp_char, "WIRELESS", strlen(temp_char))==0)) { if (eapol_wireless_get_bssid(eapol_device, eapol_dst)==-1) { xlogf(DEBUG_NORMAL, "Specific Wireless Authenticator MAC not found, using default destination\n"); } /* determine network name */ if (netid != NULL) { strncpy(eapol_ssid, netid, 99); if (strlen(netid) >= 100) { xlogf(DEBUG_NORMAL, "Network ID too long\n"); return -1; } } else if (eapol_wireless_get_ssid(eapol_device, eapol_ssid) == -1) { xlogf(DEBUG_NORMAL, "Could not determine network name, assuming there is none\n"); xlogf(DEBUG_NORMAL, "Using default network profile. (Network Name = default)\n"); strncpy(eapol_ssid,"default\0",8); } } else { xlogf(DEBUG_NORMAL, "This connection has been forced to a wired connection! The network name will be 'default'\n"); strncpy(eapol_ssid,"default\0",8); is_wired=TRUE; } } #if EAPOL_DEBUG eapol_debug("init_eapol - the ESSID", NULL, 0, eapol_ssid); #endif /* Establish our global variables. */ set_initialize(1); eapol_pae_transition_state(); // We need to transition to the correct state /* initialize lower layer */ if (init_eap(eapol_config, eapol_ssid) != 0) { xlogf(DEBUG_NORMAL, "Problems initalizing EAP methods!\n"); return -1; } // Start our alarm handler. We want to tick off every second, then call // the state machine, to make sure we are current. signal(SIGALRM, alarm_tick); alarm(1); //Start the timer. xlogf(DEBUG_NORMAL, "Done with init.\n"); return retVal; } //Return the current state of the state machine. EapolStates eapol_get_current_state() { return eapol_current_state; } //Return the last state of the state machine. EapolStates eapol_get_last_state() { return eapol_last_state; } void eapol_handle_logoff() { /* This is a kludge. We should really be checking to see if we have an interface to pass frames to, otherwise we segfault. */ if(eapol_get_current_state() == AUTHENTICATED) { set_userLogoff(1); eapol_pae_transition_state(); eapol_pae_do_state(); set_userLogoff(0); } } // Clean up int eapol_shutdown() { eapol_wireless_cleanup(); // Transition to logoff state eapol_handle_logoff(); shutdown_eap(); shutdown_frame_funcs(); xsupplicant_shutdown_auth(); return 0; } /** * Function that actually implements Supplicant PAE State machine * * return 0 on authenticated -1 failed * call this function inside the main event loop. */ int eapol_authenticate() { /* general setup */ u_char *frame_ptr = NULL; int retVal = 0; /* default to success */ /* if we have a different bssid than previously, inititialize */ if (eapol_pae_do_state() < 0) { return -1;} if (eapol_key_receive_do_state() < 0) { xlogf(DEBUG_NORMAL, "There was an error with the key packet.\n" "Authentication succeeded, but error is fatal.\n"); return -1; } /* get the next frame */ while (frame_ptr == NULL) { //Check for logoff if(get_userLogoff()) { if(eapol_get_current_state() == LOGOFF) // We're trying to logoff... return -1 { return -1; } } // If the sleep value for get_frame is too high, we will miss frames // and may fail the next authentication request. If the value is // too low, we will waste a lot of processor time, and eat laptop // batteries. if (more_frames() == 1) // We have more frames, so don't wait before { // checking. frame_ptr = get_frame(0); } else { frame_ptr = get_frame(250000); // Wait 1/4th of a second. } if (get_tick()==TRUE) { if (eapol_pae_do_state() < 0) return -1; } // We need to keep the event loop in 1xdriver.c going. // If we get this far, and the frame_ptr is NULL, we should return 0; if(frame_ptr == NULL) { return 0; } } if (frame_ptr != NULL) /* decode the packet and get the next response back*/ if((retVal = eapol_decode_packet(frame_ptr)) == -1){ return retVal; } /* Based on the received packet, go to the next state of each machine */ eapol_pae_transition_state(); eapol_key_receive_transition_state(); done_with_current_frame(); // This should destroy the frame in memory. if (frame_ptr != NULL) { frame_ptr = NULL; } return retVal; } void xsupplicant_shutdown_auth() { int kval; if (xsupplicant_auth_command_pid) { kval = kill(xsupplicant_auth_command_pid, SIGTERM); if (kval == -1 && errno != ESRCH) { xlogf(DEBUG_NORMAL, "Error killing process %d\n", xsupplicant_auth_command_pid); } xsupplicant_auth_command_pid = 0; } } /** Take a command line argument and exec to it */ int xsupplicant_exec(char *command) { char *args[10]; char *p; int index = 0; int i; int retval; if (!command) return -1; p = strtok(command, " "); args[index++] = strdup(p); while (p && (p = strtok(NULL, " ")) != NULL && index < 10) { args[index++] = strdup(p); } args[index] = NULL; retval = execvp(command, args); for (i = 0; i < index; i++) { free(args[i]); } return retval == -1? retval : 0; } /* function to do stuff after each authentication or after each authentication */ void xsupplicant_post_auth() { char *command = NULL; // if it's the first transition to auth /** XXX Use something other than popen here, we really want it to die when we close it */ if (xsupplicant_run_auth && !xsupplicant_first_auth) { if ((command = get_first_auth()) != NULL) { if (xsupplicant_auth_command_pid) xsupplicant_shutdown_auth(); xsupplicant_auth_command_pid = fork(); // the child should try to exec if (xsupplicant_auth_command_pid == 0 && xsupplicant_exec(command) == -1) { xlogf(DEBUG_NORMAL, "Failed to exectue command %s." "Failed call to exec()\n"); exit(0); } else if (xsupplicant_auth_command_pid == -1) { xlogf(DEBUG_NORMAL, "Failed to execute command %s." "Failed call to fork()\n", command); } } // never do that again xsupplicant_first_auth = 1; } // all other transitions to auth else if(xsupplicant_run_auth) { if ((command = get_after_auth()) != NULL) { if (xsupplicant_auth_command_pid) xsupplicant_shutdown_auth(); xsupplicant_auth_command_pid = fork(); // the child should try to exec if (xsupplicant_auth_command_pid == 0 && xsupplicant_exec(command) == -1) { xlogf(DEBUG_NORMAL, "Failed to exectue command %s." "Failed call to exec()\n"); exit(0); } else if (xsupplicant_auth_command_pid == -1) { xlogf(DEBUG_NORMAL, "Failed to execute command %s." "Failed call to fork()\n", command); } } } if (command) free(command); // unconditionally say we have done something xsupplicant_run_auth = 0; } /**** LOW LEVEL FUNCTIONS****/ /* Only Functions defined in EAPOL should call these */ // This function is defined by the standard (8.5.10.1.3(a)), and sends an // EAPOL-Start frame. void txStart() { char *temp; temp = eapol_create_start_stop_frame(EAPOL_START); send_frame(temp, 18); free(temp); temp = NULL; } // This function is defined by the standard (8.5.10.1.3(b)), and sends an // EAPOL-Logoff frame. void txLogoff() { char *temp; temp = eapol_create_start_stop_frame(EAPOL_LOGOFF); send_frame(temp, 18); free(temp); temp = NULL; } // This function is defined by the standard (8.5.10.1.3(c)), and sends an // EAP Response, Identity frame. void txRspId(int prevId, int revId) { const int mac_header_bytes = 14; u_char *resp_out = NULL; int resp_out_size = 0; char *temp = NULL; resp_out = (u_char *)malloc(1514); // 1514 is the largest frame we could send. memcpy(resp_out, &eapol_dst, 6); #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "eapol_dst = %x:%x:%x:%x:%x:%x\n", eapol_dst[0], eapol_dst[1], eapol_dst[2], eapol_dst[3], eapol_dst[4], eapol_dst[5]); xlogf(DEBUG_EVERYTHING, "resp_out = %x:%x:%x:%x:%x:%x\n", resp_out[0], resp_out[1], resp_out[2], resp_out[3], resp_out[4], resp_out[5]); #endif temp = get_src_mac(); if(temp == NULL) { xlogf(DEBUG_NORMAL, "ACK! temp is NULL in txRspId! (We're going to crash.)\n"); } memcpy(&resp_out[6], temp, 6); free(temp); temp = NULL; resp_out[12] = 0x88; // 0x888e is an EAPOL frame resp_out[13] = 0x8e; if (eap_build_responseId(resp_out, &resp_out_size) == -1) { free(resp_out); resp_out = NULL; xlogf(DEBUG_NORMAL, "Couldn't build EAP response! (This is BAD!)\n"); } else { #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "Sending to %x:%x:%x:%x:%x:%x\n", resp_out[0], resp_out[1], resp_out[2], resp_out[3], resp_out[4], resp_out[5]); #endif send_frame(resp_out, resp_out_size + mac_header_bytes); free(resp_out); resp_out = NULL; } } // This function is defined by the standard (8.5.10.1.3(d)), and sends an // EAP Response Authentication frame. int txRspAuth(int prevId, int revId) { const int mac_header_size = 14; u_char *resp_out=NULL; int resp_out_size=0, i=0; char *temp = NULL; char *tmp = NULL; resp_out = (u_char *)malloc(1514); // Again, this is the largest frame we can send. memcpy(resp_out, &eapol_dst, 6); #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "eapol_dst = %x:%x:%x:%x:%x:%x\n", eapol_dst[0], eapol_dst[1], eapol_dst[2], eapol_dst[3], eapol_dst[4], eapol_dst[5]); xlogf(DEBUG_EVERYTHING, "resp_out = %x:%x:%x:%x:%x:%x\n", resp_out[0], resp_out[1], resp_out[2\ ], resp_out[3], resp_out[4], resp_out[5]); #endif temp = get_src_mac(); memcpy(&resp_out[6], temp, 6); resp_out[12] = 0x88; // 0x888e for EAPOL resp_out[13] = 0x8e; free(temp); temp = NULL; tmp = get_working_frame(); // This should be the request frame. i = (((u_char) tmp[16] << 8) | (u_char)tmp[17]); // Pass in the frame, minus the header. if (eap_build_auth_response(&tmp[14], i, resp_out, &resp_out_size) == -1) { resp_out = NULL; xlogf(DEBUG_EVERYTHING, "Couldn't build EAP authentication response!\n"); return -1; } else { #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "calling send_frame with a size of %d byte(s)\n",resp_out_size+14); #endif send_frame(resp_out, resp_out_size + mac_header_size); free(resp_out); resp_out = NULL; } return 0; } void eapol_pae_set_state(EapolStates newState) { // Set the last state to the current state eapol_last_state = eapol_current_state; //Set the current state to a new state eapol_current_state = newState; } int eapol_pae_do_state() { int retVal = 0; if (get_initialize()) { eapol_pae_set_state(DISCONNECTED); } else if (get_eapSuccess() && !get_initialize()) { eapol_pae_set_state(AUTHENTICATED); xsupplicant_run_auth = 1; } else if(get_eapFail() && !get_initialize() && !get_userLogoff && !get_logoffSent()) { eapol_pae_set_state(HELD); } else if((get_userLogoff() && !get_logoffSent()) && !(get_initialize())) { eapol_pae_set_state(LOGOFF); } switch(eapol_get_current_state()) { case CONNECTING: // If we just transitioned to Connecting, we need to set the variables. // otherwise, we need to decrement, and possibly send an EAPOL-Start. if (eapol_get_last_state() != CONNECTING) { xlogf(DEBUG_STATE, "CONNECTING\n"); set_startWhen(startPeriod); set_startCount(0); set_reqId(FALSE); txStart(); } else { set_startWhen(get_startWhen() - 1); if (get_startWhen() <= 0) { set_startCount(get_startCount() + 1); set_startWhen(startPeriod); if ((get_startCount() < 3)) { xlogf(DEBUG_NORMAL, "Sending EAPOL-Start #%d\n",get_startCount()); txStart(); } else { xlogf(DEBUG_NORMAL, "No authenticator found! Assuming the port is authorized!\n"); eapol_pae_set_state(AUTHENTICATED); xsupplicant_run_auth = 1; eapol_pae_transition_state(); // This may not be needed. } } } break; case AUTHENTICATED: set_eapSuccess(FALSE); set_eapFail(FALSE); set_suppStatus(AUTHORIZED); if(eapol_get_current_state() != eapol_get_last_state()) { xlogf(DEBUG_STATE, "AUTHENTICATED\n"); } /* XXX do stuff here for what do to after every authentication and after the first */ xsupplicant_post_auth(); break; case ACQUIRED: if (get_reqId() == TRUE) // Do we have an ID request? { xlogf(DEBUG_NORMAL, "Connection Established, authenticating...\n"); txRspId(get_previousId(), get_receivedId()); set_authWhile(authPeriod); set_startCount(0); set_reqId(FALSE); set_reqAuth(FALSE); } if (get_authWhile() <= 0) { set_authWhile(authPeriod); txRspId(get_previousId(), get_receivedId()); } else { set_authWhile(get_authWhile() - 1); } set_previousId(get_receivedId()); if(eapol_get_current_state() != eapol_get_last_state()) { xlogf(DEBUG_STATE, "ACQUIRED\n"); } break; case AUTHENTICATING: if (get_reqAuth()) { set_authWhile(authPeriod); set_reqAuth(FALSE); if (txRspAuth(get_previousId(), get_receivedId()) < 0){ return -1; } } if (get_authWhile() <= 0) { set_authWhile(authPeriod); if (txRspAuth(get_previousId(), get_receivedId()) < 0) return -1; } set_previousId(get_receivedId()); if(eapol_get_current_state() != eapol_get_last_state()) { xlogf(DEBUG_STATE, "AUTHENTICATING\n"); } break; case HELD: if(eapol_get_current_state() != eapol_get_last_state()) { set_heldWhile(heldPeriod); set_eapFail(FALSE); set_eapSuccess(FALSE); set_suppStatus(UNAUTHORIZED); xlogf(DEBUG_STATE, "HELD %d -> %d\n", eapol_get_current_state(), eapol_get_last_state()); } else { if (get_heldWhile() <= 0) // We need to transition back to CONNECTING { eapol_pae_set_state(CONNECTING); } else { set_heldWhile(get_heldWhile() - 1); } } break; case LOGOFF: xlogf(DEBUG_STATE, "LOGOFF\n"); //Send a logoff txLogoff(); set_logoffSent(TRUE); set_suppStatus(UNAUTHORIZED); break; case DISCONNECTED: /* The 802.1x spec calls for an unconditional transition to CONNECTING from DISCONNECTED state */ set_eapSuccess(FALSE); set_eapFail(FALSE); set_startCount(0); set_logoffSent(FALSE); set_previousId(256); set_suppStatus(UNAUTHORIZED); set_initialize(0); eapol_pae_set_state(CONNECTING); /* automatic transition because We already setup link */ /* xsupplicant-specifc stuff */ set_haveKey(FALSE); #if EAPOL_DEBUG if(eapol_get_current_state() != eapol_get_last_state()) eapol_debug("eapol_pae_do_state", NULL, 0, "DISCONNECTED"); #endif break; default: xlogf(DEBUG_NORMAL, "Supplicant PAE state machine panic!! unkown state\n"); retVal = -1; break; } if(eapol_get_current_state() != eapol_last_state) { eapol_last_state = eapol_get_current_state(); } set_tick(FALSE); //Set our tick to false as the last thing we do. // Section (8.5.3.1.1) return retVal; } int eapol_pae_transition_state() { int retVal = 0; switch(eapol_get_current_state()) { case CONNECTING: if (get_reqId() == TRUE) { #if EAPOL_DEBUG xlogf(DEBUG_STATE, "Request ID, skipping CONNECTING state...\n"); #endif eapol_pae_set_state(ACQUIRED); } break; case ACQUIRED: if (get_reqId() == TRUE) { eapol_pae_set_state(ACQUIRED); } else if (get_reqAuth() == TRUE) { eapol_pae_set_state(AUTHENTICATING); } else if (get_authWhile() == 0) { eapol_pae_set_state(CONNECTING); } break; case AUTHENTICATING: if (get_reqId() == TRUE) { eapol_pae_set_state(ACQUIRED); } else if (get_reqAuth() == TRUE) { eapol_pae_set_state(AUTHENTICATING); } else if (get_authWhile() == 0) { eapol_pae_set_state(CONNECTING); } break; case HELD: if (get_heldWhile() == 0) { eapol_pae_set_state(CONNECTING); } if (get_reqId() == TRUE) { eapol_pae_set_state(ACQUIRED); } break; case AUTHENTICATED: if (get_reqId() == TRUE) { eapol_pae_set_state(ACQUIRED); } break; case LOGOFF: if(!get_userLogoff()) { eapol_pae_set_state(DISCONNECTED); } break; case DISCONNECTED: /* 802.1x specifies that you must unconditionally transition from DISCONNECTED to CONNECTED, but we handle this in eap_pae_do_state() in the DISCONNECTED case. */ break; } return retVal; } int eapol_key_receive_do_state() { int retVal = 0; switch(eapol_key_receive_state) { case NO_KEY_RECEIVE: break; case KEY_RECEIVE: set_rxKey(0); /* if (eapol_process_key() == -1) { */ /* retVal = -1; */ /* } */ // if (eapol_bringup_interface(eapol_device)) { // retVal = -1; // goto eapol_key_receive_do_state_END; // } break; default: xlogf(DEBUG_NORMAL, "Supplicant Key Recieve state machine panic!! unknown state\n"); retVal = -1; } return retVal; } int eapol_key_receive_transition_state() { int retVal = 0; switch(eapol_key_receive_state) { case NO_KEY_RECEIVE: if (get_rxKey() == 1) { eapol_key_receive_state = KEY_RECEIVE; } break; case KEY_RECEIVE: if (get_rxKey() == 1) { eapol_key_receive_state = KEY_RECEIVE; } else { eapol_key_receive_state = NO_KEY_RECEIVE; } break; } return retVal; } // Build an EAPOL Start frame. char * eapol_create_start_stop_frame(char stst) { u_char *eapol_start; u_char *src_addr; eapol_start = (u_char *)malloc(18); // An eapol start frame is 18 bytes. if (eapol_start == NULL) { return NULL; } memcpy(eapol_start, eapol_dst, 6); // Copy the destination address. src_addr = get_src_mac(); memcpy(&eapol_start[6], src_addr, 6); // Copy the source address. free(src_addr); src_addr = NULL; eapol_start[12] = 0x88; // 0x888e is EAPOL frame type. eapol_start[13] = 0x8e; eapol_start[14] = 1; // EAPOL_Version eapol_start[15] = stst; // Start or Stop eapol_start[16] = 0; // No payload. eapol_start[17] = 0; return eapol_start; } // Build an EAPOL NAK frame. (Still in testing phase.) char * eapol_create_nak_frame(uint8_t auth_type_requested) { u_char *eapol_start; u_char *src_addr; uint16_t length=6; eapol_start = (u_char *)malloc(25); if (eapol_start == NULL) { return NULL; } memcpy(eapol_start, eapol_dst, 6); // Copy the destination address. src_addr = get_src_mac(); memcpy(&eapol_start[6], src_addr, 6); // Copy the source address. free(src_addr); src_addr = NULL; eapol_start[12] = 0x88; // 0x888e is EAPOL frame type. eapol_start[13] = 0x8e; eapol_start[14] = 1; // EAPOL_Version eapol_start[15] = 0x00; // EAPOL_PACKET length = htons(6); memcpy(&eapol_start[16], &length, 2); eapol_start[18] = 2; //Response eapol_start[19] = get_receivedId(); memcpy(&eapol_start[20], &length, 2); eapol_start[22] = 0x03; // NAK eapol_start[23] = auth_type_requested; return eapol_start; } void print_hex_values(char *to_print, int len) { int i; for (i=0; isignature, 0, 16); eapcrypt_key_hmac(tmpbuf, keypacket_len, tmpsig); keyHeader = (struct eapol_key_header *)(keypacket + sizeof(struct eapol_hdr)); #if EAPOL_DEBUG xlogf(DEBUG_NORMAL, "the found signature: "); for (i = 0; i < 16; i++) { xlogf(DEBUG_NORMAL, "%x ", tmpsig[i]); } xlogf(DEBUG_NORMAL, "\n"); xlogf(DEBUG_NORMAL, "the real signature: "); for (i = 0; i < 16; i++) { xlogf(DEBUG_NORMAL, "%x ", keyHeader->signature[i]); } xlogf(DEBUG_NORMAL, "\n"); xlogf(DEBUG_NORMAL, "the iv: "); for (i = 0; i < 16; i++) { xlogf(DEBUG_NORMAL, "%x ", keyHeader->iv[i]); } xlogf(DEBUG_NORMAL, "\n"); #endif xlogf(DEBUG_EVERYTHING, "After iv:\n"); /* Next compare the signature to make sure we are on the same page */ if (memcmp(tmpsig, keyHeader->signature, 16) != 0) { xlogf(DEBUG_NORMAL, "Failed EAPOL key signature\n"); if (tmpbuf) free(tmpbuf); return -1; } keylen = ntohs((short)*((short *)&keyHeader->length)); xlogf(DEBUG_NORMAL, "EAPOL Key processed: %s [%d] (%d bytes) \n", keyHeader->index & EAPOL_KEY_UNICAST ? "unicast" : "broadcast", (keyHeader->index & EAPOL_KEY_INDEX)+1, keylen); xlogf(DEBUG_EVERYTHING, "After check signature...\n"); /* check if there was key material in the packet */ if (keylen != 0) { newkey = (u_char *)malloc(sizeof(u_char) * keylen); if (keypacket_len - sizeof(struct eapol_hdr) - sizeof(struct eapol_key_header) > 0) { enckey = keypacket + sizeof(struct eapol_hdr) + sizeof(struct eapol_key_header); eapcrypt_decrypt_key(enckey, newkey, keylen, keyHeader->iv, 16); eapol_wireless_set_key(eapol_device, newkey, keylen, keyHeader->index); #if EAPOL_DEBUG eapol_debug("eapol_process_key", newkey, keylen, "the decrypted key"); #endif } else { eapcrypt_get_peer_key(newkey, keylen); eapol_wireless_set_key(eapol_device, newkey, keylen, keyHeader->index); #if EAPOL_DEBUG eapol_debug("eapol_process_key", newkey, keylen, "the peer key"); #endif } if (newkey) free(newkey); } if (tmpbuf) free(tmpbuf); set_haveKey(TRUE); return retVal; } // The type of authentication we want to use. void send_eapol_nak(int type) { send_frame(eapol_create_nak_frame(type),24); } int eapol_decode_packet(u_char *in) { struct eth_hdr * ethHeader = (struct eth_hdr *)in; /* The eapol header portion */ struct eapol_hdr * theHeader = (struct eapol_hdr *)(in + ETH_HDR_LEN); /* Just the payload */ u_char * payload = in + ETH_HDR_LEN + sizeof(struct eapol_hdr); int receive_packet_size; struct eap_type_hdr *theTypeHeader = (struct eap_type_hdr *)payload; /* The payload size */ int retVal = 0; /* default to success */ int eap_return = 0; char *my_src = NULL; #ifdef NAK_SUPPORT char *temp = NULL; #endif /* Major confusion going on here --- figure out this packet len crap */ receive_packet_size = (int)ntohs(theHeader->len) + sizeof(struct eapol_hdr); my_src = get_src_mac(); if (memcmp(&in[6], my_src, ETH_ADDR_LEN) == 0) { #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "We got a packet from ourselves.\n"); #endif free(my_src); my_src = NULL; return 0; } free(my_src); my_src = NULL; /* Check for enough info */ /* if (payload_size < 0) { xlogf(DEBUG_NORMAL, "Truncated packet detected %d\n", in_size); retVal = 0; goto eapol_decode_packet_END; }*/ /* if the from address is not the old bssid, re-initialize */ if (memcmp(eapol_dst, ðHeader->eth_src, ETH_ADDR_LEN) != 0) { // If the destination host from the authenticator isn't us, then // don't change it. if (memcmp(eapol_dst, ðHeader->eth_dst, ETH_ADDR_LEN) != 0) { memcpy(eapol_dst, ðHeader->eth_src, ETH_ADDR_LEN); #if EAPOL_DEBUG xlogf(DEBUG_EVERYTHING, "the new dst IS %x:%x:%x:%x:%x:%x\n", eapol_dst[0], eapol_dst[1], eapol_dst[2], eapol_dst[3], eapol_dst[4], eapol_dst[5]); #endif } /* need to update what network we are on */ if (is_wired==FALSE) { eapol_wireless_get_ssid(eapol_device, eapol_ssid); eapol_wireless_get_bssid(eapol_device, eapol_dst); } } else { set_initialize(0); } /* first set flags based on the incoming PDU */ switch (theHeader ->eaptype) { case EAP_PACK_TYPE: /* See what EAP thinks about the packet- EAP knows whether or not to send a respId so the state machine will remain consistent, but this isn't really necessary */ set_receivedId(theTypeHeader->id); eap_return = eap_decode_packet(in); #if EAPOL_DEBUG eapol_debug("eapol_decode_packet", NULL, 0, "back from eap_return"); #endif switch (eap_return) { case -1: xlogf(DEBUG_NORMAL, "There was an error decoding the packet.\n"); retVal = -1; goto eapol_decode_packet_END; break; case 0: // Here, we need to check and see if the EAP type we want is being used. #ifndef NAK_SUPPORT set_reqAuth(TRUE); #else temp = get_preferred_auth(); if (temp == NULL) { set_reqAuth(TRUE); } else { // See if the type code matches what we want. if (theTypeHeader->type != is_valid_eap(temp)) { xlogf(DEBUG_NORMAL, "The authentication server requested a different style of authentication. (Style %d)\n", theTypeHeader->type); xlogf(DEBUG_NORMAL, "Trying to force the authentication server to use our style.\n"); send_eapol_nak(is_valid_eap(temp)); // send a NAK with the type we want to use instead. } else { set_reqAuth(TRUE); } } #endif break; case 1: set_reqId(TRUE); break; case 3: xlogf(DEBUG_NORMAL, "Authentication Succeeded\n"); set_eapSuccess(TRUE); #if EAPOL_DEBUG successCount ++; #endif break; case 4: xlogf(DEBUG_NORMAL, "Failed to Authenticate\n"); set_eapFail(TRUE); eapol_pae_set_state(HELD); #if EAPOL_DEBUG failCount ++; #endif break; } /* DO the stuff here to put together the return packet, given the payload from eap_decode */ break; case EAPOL_START: //We will probably see EAPOL-Start messages from other clients in a // shared media configuration. (Such as wireless) We should just // ignore them. retVal = 0; goto eapol_decode_packet_END; break; case EAPOL_LOGOFF: //We will probably see EAPOL-Logoff messages from other clients in a // shared media configuration. (Such as wireless) We should just // ignore them. retVal = 0; goto eapol_decode_packet_END; break; case EAPOL_KEY: set_rxKey(TRUE); eapol_process_key((u_char *)theHeader, receive_packet_size); #if EAPOL_DEBUG eapol_debug("eapol_decode_packet", NULL, 0, "Got an EAPOL KEY"); #endif break; case EAPOL_ASF_ALERT: #if EAPOL_DEBUG eapol_debug("eapol_decode_packet", NULL, 0, "Got an EAPOL ASF ALERT"); #endif xlogf(DEBUG_NORMAL, "Recieved an EAPOL Alert! (Connection is probably broken.)\n"); break; default: xlogf(DEBUG_NORMAL, "Unknown EAPOL Packet detected\n"); } eapol_decode_packet_END: //Success or Fail #if EAPOL_DEBUG if(eap_return == 3 || eap_return == 4) { xlogf(DEBUG_CONFIG, "Success Count: %d\n", successCount); xlogf(DEBUG_CONFIG, "Failure Count: %d\n", failCount); } #endif return retVal; } /*** TBD: replace this popen with a secure call ***/ int eapol_bringup_interface(char *device) { #if 0 #ifdef IFUP FILE *dhcpFILE; char dhcprequest[100] = "/sbin/dhcpcd -n "; strcat(dhcprequest, device); dhcpFILE = popen(dhcprequest, "r"); if (dhcpFILE == NULL) { xlogf(DEBUG_NORMAL, "Failed to call %s\n", dhcprequest); return -1; } pclose(dhcpFILE); xlogf(DEBUG_NORMAL, "Brought up interface %s\n", device); #else #error No Method to bring up interface found #endif #endif return 0; } const char *eapol_get_dst_mac() { return eapol_dst; } /*** EOF ***/