/**
 * 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: peap_phase2.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: peap_phase2.c,v 1.19 2004/07/15 04:15:37 chessing Exp $
 * $Date: 2004/07/15 04:15:37 $
 * $Log: peap_phase2.c,v $
 * Revision 1.19  2004/07/15 04:15:37  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.18  2004/06/29 01:35:36  chessing
 *
 * Added credit to eapleap.c for Gilbert Goodwill (who gave me the clues I needed to figure out the keying piece for LEAP).  Added patch from Toby Collett to make PEAP work for broken implementations of IAS.  (New option ias_quirk has been added.)
 *
 * Revision 1.17  2004/06/15 03:22:31  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include "config.h"
#include "profile.h"
#include "eap.h"
#include "peap_phase2.h"
#include "eappeap.h"
#include "../tls/eaptls.h"
#include "xsup_err.h"
#include "xsup_debug.h"
#include "../mschapv2/eapmschapv2.h"
#include "../otp/eapotp.h"
#include "../tls/tls_crypt.h"

#define VALID_EAP_TYPE  EAP_TYPE_MSCHAP
#define HIGHEST_PEAP_SUPPORTED   1

int set_peap_version(struct phase2_data *p2d, int new_version)
{
  if (!p2d)
    {
      debug_printf(DEBUG_NORMAL, "Invalid phase 2 data in set_peap_version()!\n");
      return XEMALLOC;
    }

  if (new_version > HIGHEST_PEAP_SUPPORTED) 
    {
      p2d->peap_version = HIGHEST_PEAP_SUPPORTED;
      return HIGHEST_PEAP_SUPPORTED;
    }

  // Only change versions if we are changing to a higher one.  This will 
  // keep us from backing off to a lower version mid-communication, should the
  // RADIUS server get confused.
  if (p2d->peap_version < new_version)
    {
      debug_printf(DEBUG_AUTHTYPES, "PEAP Version changed to %d\n",new_version);
      p2d->peap_version = new_version;
    }
  return p2d->peap_version;
}

// Remove the beginning 18 bytes.
void peap_unpad_frame(u_char *in, int in_size, u_char *out, int *out_size)
{
  int i;

  if ((!in) || (!out))
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet buffer in in or out at peap_unpad_frame()!\n");
      return;
    }

  if (in_size > 1520)
    {
      debug_printf(DEBUG_NORMAL, "Packet too large in peap_unpad_frame()!\n");
      return;
    }

  *out_size = in_size - 4;

  for (i=0;i<=*out_size;i++)
    {
      out[i] = in[4+i];
    }
}

// Pad out the beginning with 18 bytes.  (Probably 0s.)
void peap_pad_frame(u_char *in, int in_size, u_char *out, int *out_size)
{
  int i;

  if ((!in) || (!out))
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet buffer in in or out at peap_pad_frame()!\n");
      return;
    }

  if (in_size > 1520)
    {
      debug_printf(DEBUG_NORMAL, "In packet size to large!  Ignoring!\n");
      return;
    }

  *out_size = in_size + 4;

  bzero(out, *out_size);
  for (i=0;i<=in_size;i++)
    {
      out[4+i] = in[i];
    }
}

void do_peap_version1(struct phase2_data *thisint, u_char *in, int in_size, 
		      u_char *out, int *out_size)
{
  char *new_frame = NULL;
  int eapvalue, eapid;
  struct tls_vars *mytls_vars;

  if ((!thisint) || (!out) || (!out_size))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to do_peap_version1()!\n");
      if (out_size) *out_size = 0;
      return;
    }

  if (in_size > 1520)
    {
      debug_printf(DEBUG_NORMAL, "Invalid frame passed in to do_peap_version1()!\n");
      return;
    }
  
  *out_size = 0;

  mytls_vars = (struct tls_vars *)thisint->eapdata->eap_data;
  // mytls_vars may be NULL here!  This is okay, as long as we aren't in the
  // middle of an inner authentication.

  eapvalue = in[4];
  eapid = in[5]; // Inner EAP id.

  debug_printf(DEBUG_AUTHTYPES, "Inner packet : \n");
  if (in_size < 1522)
    {
      debug_hex_dump(DEBUG_AUTHTYPES, in, in_size);
    } else {
      debug_printf(DEBUG_AUTHTYPES, "INVALID PACKET SIZE!\n");
    }

  new_frame = (char *)malloc(1024);
  if (new_frame == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't allocate memory for new_frame in do_peap_version1()!\n");
      return;
    }
  bzero(new_frame, 1024);

  switch ((uint8_t)eapvalue)
    {
    case EAP_REQUEST:
      eap_request_id(thisint->eapdata->identity, eapid, out, out_size); 
      break;

    case EAP_SUCCESS:
      printf("Got a phase 2 success!\n");
      break;

      // If we catch the EAP_FAILURE, it has the same value as MD5.  Which
      // is probably why PEAP-EAP-MD5 isn't allowed in XP. ;)
      /*
    case EAP_FAILURE:
      printf("Got a phase 2 failure!\n");
      break;
      */

    case PEAP_EAP_EXTENSION: // EAP Extension
      debug_printf(DEBUG_AUTHTYPES, "Got an EAP extension frame!\n");
      out[0] = EAP_RESPONSE;
      memcpy(&out[1], &in[1], in_size-1);
      *out_size = in_size;
      break;

    default:
      if (eap_create_active_method(&thisint->activemethod,
				   thisint->eapdata->identity,
				   thisint->eapdata->tempPwd) != 0)
	{
	  debug_printf(DEBUG_NORMAL, "Couldn't build active method!  Phase 2 authentication will not happen!\n");
	  return;
	}
      eap_request_auth(thisint->activemethod, thisint->eapdata->eap_conf_data,
		       in, in_size, out, out_size);
      break;
    }
  free(new_frame);
}

void do_peap_version0(struct phase2_data *thisint, u_char *in, int in_size, 
		      u_char *out, int *out_size)
{
  char *padded_frame, *new_frame;
  int padded_size, new_frame_size, eframe = 0;

  if ((!thisint) || (!out_size))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to do_peap_version0()!\n");
      if (out_size) *out_size = 0;
      return;
    }

  *out_size = 0;

  if (!in)
    {
      debug_printf(DEBUG_NORMAL, "Input frame was NULL!  Ignoring!\n");
      return;
    }

  if (!out)
    {
      debug_printf(DEBUG_NORMAL, "Invalid return buffer!\n");
      return;
    }

  if (in_size>1520)
    {
      debug_printf(DEBUG_NORMAL, "Input frame is too big! Ignoring!\n");
      *out_size = 0;
      return;
    }

  padded_size = in_size;

  padded_frame = (char *)malloc(in_size+19);  // It is 19 bytes to pad out.
  if (padded_frame == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Unable to allocate memory for padded_frame in do_peap_version0()!\n");
      return;
    }

  if ((in[4] == 0x21) && (in[5] = 0x80) && !(thisint->eapdata->ias_quirk == 1 && in[0] == EAP_TYPE_MSCHAPV2))  
    {
      eframe = 1;
      memcpy(padded_frame, in, in_size);
    }

  if (eframe != 1) 
    {
      peap_pad_frame(in, in_size, padded_frame, &padded_size);
    }

  new_frame = (char *)malloc(1024);
  if (new_frame == NULL)
    {
      debug_printf(DEBUG_NORMAL, "ACK!  We can't allocate memory!\n");
      return;
    }
 
  do_peap_version1(thisint, padded_frame, padded_size, new_frame, 
		   &new_frame_size);
  free(padded_frame);
  if (eframe !=1) 
    {
      peap_unpad_frame(new_frame, new_frame_size, out, out_size);
    } else {
      memcpy(out, new_frame, new_frame_size);
      *out_size = new_frame_size;
    }
  free(new_frame);

  eframe = 0;
}


void peap_do_phase2(struct generic_eap_data *thisint, u_char *in, int in_size, 
		    u_char *out, int *out_size)
{
  struct tls_vars *mytls_vars;
  struct phase2_data *p2d;
  u_char *decr_data, *encr_data;
  int encrsize, decrsize;
  struct config_eap_peap *peapconf;
  struct generic_eap_data *eapdata;

  if ((!thisint) || (!in) || (!out))
    {
      debug_printf(DEBUG_NORMAL, "Invalid parameters passed in to peap_do_phase2()!\n");
      return;
    }

  *out_size = 0;

  mytls_vars = (struct tls_vars *)thisint->eap_data;

  if (mytls_vars == NULL)
    {
      debug_printf(DEBUG_NORMAL, "mytls_vars (thisint->eap_data) == NULL!\n");
      return;
    }

  peapconf = (struct config_eap_peap *)thisint->eap_conf_data;

  if (peapconf == NULL)
    {
      debug_printf(DEBUG_NORMAL, "peapconf == NULL!\n");
      return;
    }

  p2d = (struct phase2_data *)mytls_vars->phase2data;

  if (p2d->eapdata == NULL)
    {
      p2d->eapdata = (struct generic_eap_data *)malloc(sizeof(struct generic_eap_data));
      if (p2d->eapdata == NULL)
	{
	  *out_size = 0;
	  return;
	}
      memset(p2d->eapdata, 0, sizeof(struct generic_eap_data));
      
      p2d->eapdata->eap_data = NULL;
    }

  p2d->eapdata->eap_conf_data = peapconf->phase2;
  p2d->eapdata->identity = thisint->identity;
  p2d->eapdata->ias_quirk = peapconf->ias_quirk;

  decr_data = (char *)malloc(1550);
  if (decr_data == NULL) 
    {
      debug_printf(DEBUG_NORMAL, "Couldn't allocate memory for decryption buffer!\n");
      return;
    }

  encr_data = (char *)malloc(1550);
  if (encr_data == NULL) 
    {
      debug_printf(DEBUG_NORMAL, "Couldn't allocate memory for encryption buffer!\n");
      free(decr_data);
      return;
    }

  if (in_size > 0)
    {
      tls_crypt_decrypt(thisint, in, in_size, decr_data, &decrsize);

      debug_printf(DEBUG_AUTHTYPES, "Decrypted dump : \n");
      debug_hex_dump(DEBUG_AUTHTYPES, decr_data, decrsize);
    } else {
      free(decr_data);
      decr_data = NULL;
    }

  // We need to check this.  I don't think it is needed anymore.
  if (decrsize <=0)
    {
      debug_printf(DEBUG_AUTHTYPES, "Sending ACK!\n");
      bzero(out,10);
      *out_size = 1;
      free(decr_data);
      free(encr_data);
      return;
    }

  debug_printf(DEBUG_AUTHTYPES, "Decrypted packet returned %d byte(s)\n", decrsize);

  if (thisint->tempPwd != NULL)
    {
      eapdata = p2d->eapdata;

      if (eapdata != NULL)
	{
	  eapdata->tempPwd = thisint->tempPwd;
	}
    }

  bzero(out, 100);
  switch (p2d->peap_version)
    {
    case 0:
      debug_printf(DEBUG_AUTHTYPES, "Doing PEAP v0!\n");
      do_peap_version0(p2d, decr_data, decrsize, encr_data, &encrsize);
      break;
    case 1:
      debug_printf(DEBUG_AUTHTYPES, "Doing PEAP v1!\n");
      do_peap_version1(p2d, decr_data, decrsize, encr_data, &encrsize);
      break;
    default:
      debug_printf(DEBUG_NORMAL, "Unknown PEAP version!  (%d)\n",p2d->peap_version);
      break;
    }

  eapdata = p2d->eapdata;
  
  if (eapdata->need_password == 1)
    {
      thisint->need_password = 1;
      thisint->eaptype = eapdata->eaptype;
      thisint->eapchallenge = eapdata->eapchallenge;
      *out_size = 0;
    }

  if (encrsize > 0)
    {
      debug_printf(DEBUG_AUTHTYPES, "Unencrypted return frame : \n");
      debug_hex_dump(DEBUG_AUTHTYPES, encr_data, encrsize);
      tls_crypt_encrypt_nolen(thisint, encr_data, encrsize, out, out_size);
      debug_printf(DEBUG_AUTHTYPES, "Encrypted return frame : \n");
      debug_hex_dump(DEBUG_AUTHTYPES, out, *out_size);
    }

  free(encr_data);
  free(decr_data);
}


void peap_phase2_failed(struct generic_eap_data *thisint)
{
  struct tls_vars *mytls_vars;
  struct phase2_data *p2d;

  if (!thisint)
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed to peap_phase2_failed()!\n");
      return;
    }

  mytls_vars = (struct tls_vars *)thisint->eap_data;

  if (mytls_vars == NULL)
    {
      debug_printf(DEBUG_NORMAL, "mytls_vars (thisint->eap_data) == NULL!\n");
      return;
    }

  p2d = (struct phase2_data *)mytls_vars->phase2data;

  if (p2d->eapdata == NULL)
    {
      // We didn't get to phase 2, so just bail.
      return;
    }

  eap_do_fail(p2d->activemethod);
}
