/**
 * A client-side 802.1x implementation 
 *
 * 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.
 */

/*******************************************************************
 * The driver function for a Linux application layer EAPOL 
 * implementation
 * File: xsup_driver.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: xsup_driver.c,v 1.52 2004/08/15 04:29:34 chessing Exp $
 * $Date: 2004/08/15 04:29:34 $
 * $Log: xsup_driver.c,v $
 * Revision 1.52  2004/08/15 04:29:34  chessing
 *
 * Completed support for scanning and joining wireless networks.  If your interface isn't configured, we will scan to gather information about all known wireless networks.  If a network in the list has a configuration, we will join it.  (Including turning on needed encryption.)  There are still a few rough edges that need to be hammered out.  But the overall user experience should be improved.
 *
 * Revision 1.51  2004/08/13 04:20:00  chessing
 *
 * Accepted fork() patch from i3keba.  (Slightly modified version of patch.)
 *
 * Revision 1.50  2004/08/11 04:01:17  chessing
 *
 * The -d option now supports letter designations for each debug level, in addition to 0..7.  (However, both letters and numbers can't be used at the same time!)  Also changed some formatting for the SNMP scoreboard.
 *
 * Revision 1.49  2004/08/05 23:56:18  chessing
 *
 * Added basic support for scanning for broadcast SSIDs.  This is another step closer to WPA/11i support. ;)
 *
 * Revision 1.48  2004/07/25 19:36:40  chessing
 * Fixed a few more logical/bitwise nots.  Added rtnetlink support for hot-plugging interfaces.  (Hot unplugging is coming soon. ;)
 *
 * Revision 1.47  2004/07/23 04:05:49  chessing
 * Fixed a segfault problem.  Started to add rtnetlink support.
 *
 * Revision 1.46  2004/07/23 02:14:38  chessing
 * Moved main event loop from xsup_driver.c in to an OS specific file to allow for easier porting.
 *
 * Revision 1.45  2004/07/21 22:47:53  chessing
 *
 * Fixed bitwise not problem identified in bugid 995523.
 *
 * Revision 1.44  2004/07/19 02:43:16  chessing
 *
 * Changed things to get rid of Linux specific pieces in the interface_data struct. Fixed up EAP-SIM and EAP-AKA to skip AVPs that we don't support.  (We print a mesage, and move on.)  Added the --enable-radiator-test option to configure EAP-AKA to use the test vectors included with Radiator's AKA module.  (To use this mode, no SIM card is required, but the PCSC library is still needed to build.  Also, some errors will be displayed.)
 *
 * Revision 1.43  2004/07/15 04:15:35  chessing
 *
 * True/false int values are now bit flags in a byte.  PEAP now calls back in to functions from eap.c for phase 2 methods.  This allows for any phase 1 EAP type to work.  This resulted in some major changes the functions in eap.c, and in peap_phase2.c.  PEAP has been tested using both MS-CHAPv2, and EAP-MD5 as inner types.  More types need to be tested, and enabled.
 *
 * Revision 1.42  2004/07/01 01:27:44  chessing
 *
 * Added some patches submitted to sourceforge by users.  Including openssl static/dynamic issue, and zombie children.
 *
 * Revision 1.41  2004/06/21 05:19:12  chessing
 *
 * Added a few minor fixes to EAP-AKA support.  Added "allmulti" as a global configuration option.  (By default, allmulti is now on!)
 *
 * Revision 1.40  2004/06/15 03:22:29  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/
/***
 *** This code implements 802.1X Authentication on a supplicant
 *** and supports multiple Authentication types.  
 *** See IEEE Draft P802.1X/D11, March 27, 2001 for more details
 ***/

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <strings.h>
#include <string.h>
#include <sys/wait.h>

#include "profile.h"
#include "config.h"
#include "eap.h"
#include "statemachine.h"
#include "xsup_ipc.h"
#include "xsup_debug.h"
#include "xsup_err.h"
#include "eapol.h"
#include "cmd_handler.h"
#include "cardif/cardif.h"
#include "cardif/core.h"

int wpa_keying = 0;
char pidfileName[25] = "/var/run/xsupplicant";
struct interface_data *int_list;
int dsd = 0;


/****************************************
 *
 * Create a PID file, and populate it with our PID, and the socket number
 * to be used to talk to the daemon.
 *
 ****************************************/
int create_pidfile(int socknum)
{
  FILE *pidfile;

  pidfile = fopen(pidfileName, "w");
  if (pidfile)
    {
      fprintf(pidfile, "%d", getpid());
      fclose(pidfile);
      return TRUE;
    }
  return FALSE;
}

/****************************************
 *
 * Delete a PID file.  Should only be called from global_deinit!
 *
 ****************************************/
int delete_pidfile()
{
  unlink(pidfileName);

  return TRUE;
}

/****************************************
 *
 * Test for a PID file, and return an error if something seems to be running.
 *
 ****************************************/
int is_other_xsup_running()
{
  FILE *pidfile;

  pidfile = fopen(pidfileName, "r");
  if (pidfile)
    {
      fclose(pidfile);
      return TRUE;
    }
  return FALSE;
}

/*********************************************
 *
 * When the alarm clock is called, we need to go through all interfaces, and
 * set "tick" to true.
 *
 *********************************************/
void alrmclock()
{
  struct interface_data *intcur;

  intcur = int_list;

  // Loop through all of the interfaces we are working with, and set the tick
  // value!
  while (intcur != NULL)
    {
      intcur->statemachine->tick = TRUE;
      event_core_clock_tick(intcur);
      intcur = intcur->next;
    }

  alarm(1);
}

/*********************************************
 *
 * Add the interfaces that are in our allow list, that weren't already
 * discovered.  In general, this isn't too interesting, but there may be
 * circumstances where an interface isn't recognized where 802.1x should
 * happen.  An example might be authenticating to a switch that has 
 * multiple vlans, with each vlan requiring 802.1x authentication.  In this
 * case, subinterfaces for vlans will probably not be detected, and will
 * require manual definition in the config file.
 *
 *********************************************/
void add_other_allowed_interfaces(struct interface_data **myint)
{
  struct config_string_list *allowints, *icur;
  struct interface_data *cur;

  if (!myint)
    {
      return;
    }

  allowints = config_allowed_interfaces();

  // If there isn't anything to do, then just bail.
  if (allowints == NULL) return;

  icur = allowints;

  // If we don't have any interfaces in the list, we start by adding the 
  // first one outside the loop.
  if (*myint == NULL)
    {
      // This interface isn't in our list, so add it.
      *myint = (struct interface_data *)malloc(sizeof(struct interface_data));
      if (*myint == NULL) 
	{
	  debug_printf(DEBUG_NORMAL, "Error allocating memory!  Your allow list will be ignored!\n");
	  return;
	}
      cur = *myint;
      memset(cur, 0, sizeof(struct interface_data));
	  
      if (init_interface_struct(cur, icur->name) != XENONE)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't initialize the interface struct!\n");
	  return;
	}

      cardif_init(cur);
      debug_printf(DEBUG_NORMAL, "Interface %s initalized!\n",cur->intName);

      //      cur->flags = (cur->flags & (~IS_WIRELESS));
      UNSET_FLAG(cur->flags, IS_WIRELESS);

      cur->userdata = config_build(cur->cur_essid);
      config_set_globals(cur);
      cur->next = NULL;
    }

  while (icur != NULL)
    {
      cur = *myint;

      // Check to see if this allowed interface is already set up.
      while ((cur != NULL) && (strcmp(icur->name, cur->intName) != 0))
	{
	  cur = cur->next;
	}

      if (cur == NULL)
	{
	  // This isn't in our list...
	  cur = *myint;

	  while (cur->next != NULL) cur = cur->next;

	  cur->next = (struct interface_data *)malloc(sizeof(struct interface_data));
	  if (cur->next == NULL)
	    {
	      debug_printf(DEBUG_NORMAL, "Memory allocation error!  Your allowed interfaces will not be added!\n");
	      return;
	    }

	  cur = cur->next;
	  memset(cur, 0, sizeof(struct interface_data));
	  
	  if (init_interface_struct(cur, icur->name) != XENONE)
	    {
	      debug_printf(DEBUG_NORMAL, "Couldn't init interface struct for interface %s! Cannot continue!\n", icur->name);
	    }

	  cardif_init(cur);
	  debug_printf(DEBUG_NORMAL, "Interface %s initalized!\n",cur->intName);
      
	  //          cur->flags = (cur->flags & (~IS_WIRELESS));
	  UNSET_FLAG(cur->flags, IS_WIRELESS);

	  cur->userdata = config_build(cur->cur_essid);
	  config_set_globals(cur);
	  cur->next = NULL;
	}
      icur = icur->next;
    }
}

/*********************************************
 *
 * Initialize all of the pieces that will be needed for our supplicant.
 * We need to initialize in the correct order, in order to make sure
 * that other pieces of the initialization happen with the correct 
 * information available.
 *
 * THIS FUNCTION SHOULD NEVER BE CALLED OUTSIDE OF THIS FILE!
 *
 *********************************************/
int global_init(int xdaemon, char *device, char *config)
{
  struct interface_data *intcur;
  struct config_string_list *denyints;
  int index, retVal, denyint;
  char intName[16];
  char *default_cfg = "/etc/xsupplicant.conf";

  // Set our debug level.
  int_list = NULL;
  intcur = NULL;

  debug_setdaemon(xdaemon);

  if (config == NULL) 
    {
      printf("Using default config!\n");
      config = default_cfg;
    }

  // Build up our config information.
  switch(config_setup(config))
    {
    case XECONFIGFILEFAIL:
      debug_printf(DEBUG_NORMAL, "\tPlease ensure that \"%s\" contains a valid xsupplicant configuration.\n", config);
      exit(255);
      break;
    case XECONFIGPARSEFAIL:
      debug_printf(DEBUG_NORMAL, "There was a problem with the config file.  We cannot continue.\n");
      exit(255);
      break;
    }

  logfile_setup(config_get_logfile());

  // Get a list of interfaces we allow, and deny.
  denyints = config_denied_interfaces();

  // Initalize the event core.
  event_core_setup();

  if (xdaemon > 0)
    {
      // If we are a daemon, we ignore any passed in information,
      // enumerate interfaces, then build configs.
      index = 0;
      
      while (cardif_get_int(index, (char *)&intName) != XNOMOREINTS)
	{
	  // We know we have a valid int, so check and make sure it isn't in
	  // our list of denied interfaces.
	  denyint = FALSE;

	  if (denyints != NULL)
	    {
	      // Check to see if this interface is in our list of 
	      //  interfaces to ignore.
	      denyint = config_int_in_list(intName, denyints);
	    } else {
	      debug_printf(DEBUG_CONFIG, "List of denied interfaces is empty! All interfaces will be used!\n");
	    }

	  if (denyint == FALSE)
	    {
	      if (cardif_validate(intName) == TRUE)
		{
		  retVal = 0;
		  if (int_list == NULL)
		    {
		      int_list = (struct interface_data *)malloc(sizeof(struct interface_data));
		      if (int_list == NULL) return XEMALLOC;
		      memset(int_list, 0, sizeof(struct interface_data));
		      
		      if (init_interface_struct(int_list, (char *)&intName) != XENONE)
			{
			  debug_printf(DEBUG_NORMAL, "Couldn't init interface struct %s!!!! Cannot continue!\n");
			  exit(1);
			}

		      intcur = int_list;
		      
		    } else {
		      
		      intcur->next = (struct interface_data *)malloc(sizeof(struct interface_data));
		      if (intcur->next == NULL) return XEMALLOC;
		      memset(intcur->next, 0, sizeof(struct interface_data));
		      
		      intcur = intcur->next;
		      if (init_interface_struct(intcur, (char *)&intName) != XENONE)
			{
			  debug_printf(DEBUG_NORMAL, "Couldn't init interface stuct for interface %s!!  Cannot continue!\n", intName);
			  exit(1);
			}
		    }
		  intcur->next = NULL;

		  if (cardif_init(intcur) < 0)
		    {
		      retVal = XENOTINT;
		    } else {
		      debug_printf(DEBUG_NORMAL, "Interface %s initalized!\n",intcur->intName);

		      // Check to see if this interface is wireless.
		      if (cardif_int_is_wireless(intcur->intName) == TRUE)
			{
			  SET_FLAG(intcur->flags, IS_WIRELESS);
			  cardif_check_dest(intcur);
			  cardif_start_wireless_scan(intcur);
			} else {
			  UNSET_FLAG(intcur->flags, IS_WIRELESS);
			}

		      intcur->userdata = config_build(intcur->cur_essid);
		      config_set_globals(intcur);
		      intcur->next = NULL;
		    }
		} else {
		  debug_printf(DEBUG_NORMAL, "Invalid interface %s\n",intName);
		}
	    } else {
	      debug_printf(DEBUG_INT, "Interface %s will be ignored!\n", intName);
	    }
	  index++;
	  bzero(&intName, 16);
	} 

      // Here, we need to check, and add interfaces that are included in the
      // allow, but were not found already. 
      add_other_allowed_interfaces(&int_list);

    } else {

      // We don't check if the interface is in our allow, or deny list when
      //  we are only working with one interface.

      if (!device) {
	debug_printf(DEBUG_NORMAL, "No interface provided!\n");
	return XENOTINT;
      }
      int_list = (struct interface_data *)malloc(sizeof(struct interface_data));
      if (int_list == NULL) return XEMALLOC;
      memset(int_list, 0, sizeof(struct interface_data));

      // Start by setting up the structure, and assigning the interface.
      if (init_interface_struct(int_list, device) != XENONE)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't init interface struct for device %s!  Cannot continue!\n", device);
	  exit(1);
	}

      // Establish a handler to the interface.
      if (cardif_init(int_list) < 0) return XENOTINT;
      debug_printf(DEBUG_NORMAL, "Interface initalized!\n");
  
      // Check to see if this interface is wireless.
      if (cardif_int_is_wireless(int_list->intName) == TRUE)
	{
	  SET_FLAG(int_list->flags, IS_WIRELESS);
	  cardif_check_dest(int_list);
	  cardif_start_wireless_scan(int_list);
	} else {
	  UNSET_FLAG(int_list->flags, IS_WIRELESS);
	}

      int_list->userdata = config_build(int_list->cur_essid);
      config_set_globals(int_list);
      int_list->next = NULL;

      // Then, initialize EAPoL (which will initialize EAP methods).
    }

  return XENONE;
}

void global_sigseg()
{
    fprintf(stderr, "[FATAL] Coredump!!!\n");
    fflush(stderr); fflush(stdout);
    delete_pidfile();
    exit(-1);
}

/****************************************
 *
 * Clean up any values that we have set up for use by the supplicant.  This
 * includes calling any clean up routines for other modules such as EAPoL
 * or EAP.
 *
 * THIS FUNCTION SHOULD NEVER BE CALLED OUTSIDE THIS FILE!
 *
 ****************************************/
void global_deinit()
{
  struct interface_data *intcur;
  char logoff[128];
  int logoffsize = 0;

  // First thing we need to do is kill the alarm clock, so it doesn't try
  // to do something while we are in the middle of quitting! ;)
  alarm(0);

  intcur = int_list;

  if (intcur)
    {
      xsup_ipc_cleanup(int_list);
      eapol_cleanup(int_list);
    }

  // We are going to want to send logoffs for each interface.  But, the
  // logoff frame will be the same, with different source/dest addresses.
  // Since sendframe() will fill in the source/dest, we only need one copy
  // of the logoff.
  txLogoff((char *)&logoff, &logoffsize);

  // We need to change this when we handle multple interfaces.
  while (intcur != NULL)
    {
      debug_printf(DEBUG_INT, "Sending Logoff for int %s!\n",intcur->intName);
      sendframe(intcur, (char *)&logoff, logoffsize);
      cardif_deinit(intcur);
      intcur=destroy_interface_struct(intcur);
    }

  event_core_cleanup();

  config_destroy();

  logfile_cleanup();

  debug_printf(DEBUG_STATE, "Deleting PID File...\n");

  delete_pidfile();

  exit(0);
}

/****************************************
 *
 * Reap dead children
 *
 ****************************************/

void global_child(int signum)
{
	int pid,status;
	while (1) {
		pid = waitpid (WAIT_ANY, &status, WNOHANG);
		if(pid <= 0){
			break;
		}
	}
}



/****************************************
 *
 * Display our usage information.
 *
 ****************************************/
void usage(char *prog)
{
  debug_printf(DEBUG_NORMAL, "Usage: %s "
	       "[-W] "
	       "[-c config file] "
	       "[-i device] "
	       "[-d debug_level] "
	       "[-f] "
	       "\n", prog);

  debug_printf(DEBUG_NORMAL, " <debug_level> can be any of : \n");
  debug_printf(DEBUG_NORMAL, "\t0..7 : Old style debug flags.\n");
  debug_printf(DEBUG_NORMAL, "\tA : Enable ALL debug flags.\n");
  debug_printf(DEBUG_NORMAL, "\tc : Enable CONFIG debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\ts : Enable STATE debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\ta : Enable AUTHTYPES debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\ti : Enable INT debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\tn : Enable SNMP debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\te : Enable EVERYTHING debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\tx : Enable EXCESSIVE debug flag.\n");
  debug_printf(DEBUG_NORMAL, "\nValues [0..7] cannot be combined with character attributes!\n");
}

/***************************************
 *
 * The main body of the program.  We should keep this simple!  Process any
 * command line options that were passed in, set any needed variables, and
 * enter the loop to handle sending an receiving frames.
 *
 ***************************************/
int main(int argc, char *argv[])
{
  int op, pid;
  char *theOpts = "c:i:d:Wf";
  char *config = NULL, *device = NULL;
  int xdaemon = 1, new_debug;
  struct interface_data *intcur = NULL;

  if (is_other_xsup_running() == TRUE)
    {
      debug_printf(DEBUG_NORMAL, "You can only run one instance of XSupplicant!\n");
      debug_printf(DEBUG_NORMAL, "If you are sure that no other copy of XSupplicant is running, please delete /var/run/xsupplicant!\n");
      exit(-1);
      } 

  new_debug = 0;
  config = NULL;

  // Process any arguments we were passed in.
  while ((op = getopt(argc, argv, theOpts)) != EOF) 
    {
      switch (op)
	{
	case 'c':
	  // Path to config file.
	  config = optarg;
	  break;

	case 'i':
	  // Interface to use.
	  device = optarg;
	  xdaemon = 0;
	  break;

	case 'd':
	  // Set the debug level.
	  if ((atoi(optarg) == 0) && (optarg[0] != '0'))
	    {
	      debug_alpha_set_flags(optarg);
	    } else {
	      debug_set_flags(atoi(optarg));
	    }
	  break;

	case 'W':
	  // Provide WPA keying material (PMK) to wpa_supplicant.
	  wpa_keying = 1;
	  break;

	case 'f':
	  // Force running in the foreground.
	  xdaemon = 2;
	  break;

	  // added by npetroni, need to do something with bad options.
	  // for now, I say exit.
	default:
	  usage(argv[0]);
	  exit(0);
	  break;
	}
    }

  if (xdaemon == 1)
    {
      printf("Starting XSupplicant v. %s!\n",VERSION);
      // We should fork, and let the parent die.
      pid = fork();
      
      if (pid > 0) 
	{
	  // If we are the parent, die.
	  exit(0);
	}
      
      // Otherwise, keep going.
    }

  // We have our options set, to initalize, then go in to our event loop.
  if (global_init(xdaemon, device, config) != 0)
    {
      printf("Couldn't initalize!!!!\n");
      exit(255);
    }

  if (int_list == NULL)
    {
      printf("No valid interface found!\n");
      global_deinit();
      exit(255);
    }

  if (xsup_ipc_init(int_list) != 0)
    {
      printf("Couldn't initalize daemon socket!\n");
      global_deinit();
      exit(255);
    }

  // When we quit, cleanup.
  signal(SIGTERM, global_deinit);
  signal(SIGINT, global_deinit);
  signal(SIGQUIT, global_deinit);
  signal(SIGKILL, global_deinit);
  signal(SIGSEGV, global_sigseg);

  // Create our pidfile.
  // int_list->sockInt isn't really what we want to pass in here.  We *Really*
  // want to pass in the socket # that the client should talk to us on.  This
  // will allow us to be socket agnostic.
  if(!create_pidfile(0))
    {
      printf("Couldn't create pid file!\n");
    }

  // Set up a handler, and start our timer.
  signal(SIGALRM, alrmclock);
  alarm(1);

  // we handle our dead children
  signal(SIGCHLD, global_child);

  // We just started, so execute our startup command.
  intcur = int_list;
  while (intcur != NULL)
    {
      cmd_handler_exec(intcur, config_get_startup_cmd());
      intcur = intcur->next;
    }

  while (1)
    {
      intcur = int_list;

      xsup_ipc_process(intcur);

      // There is a chance that we will need to modify the interface
      // structure inside of the event core.  (Examples include adding
      // an interface, or removing one.)  Since this is OS specific, we
      // need to call it in the event routines.
      event_core(&intcur);
    }

  return XENONE;
}
