/**
 * 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.
 */

/*******************************************************************
 *
 * File: ipc_callout.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: ipc_callout.c,v 1.12 2004/07/15 04:15:35 chessing Exp $
 * $Date: 2004/07/15 04:15:35 $
 * $Log: ipc_callout.c,v $
 * Revision 1.12  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.11  2004/06/15 03:22:29  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/
#include <netinet/in.h>
#include <strings.h>
#include <string.h>

#include "profile.h"
#include "config.h"
#include "xsup_debug.h"
#include "xsup_ipc.h"
#include "ipc_callout.h"

/*******************************************************************
 *
 * Fill in the next command record with the authentication state of the
 * selected interface.
 *
 *******************************************************************/
void ipc_callout_auth_state(struct interface_data *thisint, int *bufptr,
			    char *buffer, int bufsize, char *resbuf, 
			    int *resbufptr)
{
  struct ipc_cmd *cmd;
  struct ipc_header *header;

  if ((!thisint) || (!bufptr) || (!buffer) || (!resbuf) || (!resbufptr) ||
      (!thisint->statemachine))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_auth_state()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  if (cmd->attribute != AUTH_STATE) 
    {
      debug_printf(DEBUG_NORMAL, "Incorrect call to ipc_callout_auth_state!\n");
    }

  if (cmd->len != 0)
    {
      debug_printf(DEBUG_NORMAL, "Invalid length!  This will be the last request we answer!\n");
      *bufptr = bufsize;
    }
  *bufptr+=3;

  // Build the actual answer.
  cmd = (struct ipc_cmd *)&resbuf[*resbufptr];
  cmd->attribute = AUTH_STATE;
  cmd->len = 1;
  *resbufptr+=2;

  resbuf[*resbufptr] = thisint->statemachine->curState;
  *resbufptr+=1;

  header->numcmds++;
}

/*****************************************************************
 *
 * Get or set config values.
 *
 *****************************************************************/
void ipc_callout_process_conf(struct interface_data *thisint, int *bufptr,
			      char *buffer, int bufsize, char *resbuf, 
			      int *resbufptr)
{
  struct ipc_cmd *cmd;
  struct ipc_header *header;

  if ((!thisint) || (!bufptr) || (!buffer) || (!resbuf) || (!resbufptr))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_process_conf()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  if (cmd->attribute != CONFIG) 
    {
      debug_printf(DEBUG_NORMAL, "Incorrect call to ipc_callout_process_conf!\n");
    }

  *bufptr += sizeof(struct ipc_cmd);


}

/****************************************************************
 *
 * Register a client to receive pushed messages from the daemon.
 *
 ****************************************************************/
void ipc_callout_reg_client(struct ipc_struct *ipc, int *bufptr,
			    char *buffer, int bufsize, char *resbuf, 
			    int *resbufptr, struct sockaddr *mysock)
{
  struct ipc_cmd *cmd;
  struct registered_clients *cur;
  struct ipc_header *header;

  if ((!bufptr) || (!buffer) || (!resbuf) || (!resbufptr) || (!mysock))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_reg_client()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  if (cmd->attribute != REGISTER) 
    {
      debug_printf(DEBUG_NORMAL, "Incorrect call to ipc_callout_reg_client!\n");
    }

  if (cmd->len != 0)
    {
      debug_printf(DEBUG_NORMAL, "Invalid length!  This will be the last request we answer! (From this packet)\n");
      *bufptr = bufsize;
    }
  *bufptr+=3;

  // Now register the client.  And, assuming we register correctly, return
  // an ACK.
  if (ipc->reged == NULL)
    {
      ipc->reged = (struct registered_clients *)malloc(sizeof(struct registered_clients));
      cur = ipc->reged;
    } else {
      cur = ipc->reged;

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

      cur->next = (struct registered_clients *)malloc(sizeof(struct registered_clients));
      cur = cur->next;
    }
  
  cur->addr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
  memcpy(cur->addr, mysock, sizeof(struct sockaddr));
  cur->next = NULL;

  cmd = (struct ipc_cmd *)&resbuf[*resbufptr];

  cmd->attribute = REGISTER;
  cmd->len = 1;
  
  *resbufptr += 2;

  resbuf[*resbufptr] = ACK;
  *resbufptr += 1;

  header->numcmds++;
}

/****************************************************************
 *
 *  Return a comma seperated list of interfaces we know about.
 *
 ****************************************************************/
void ipc_callout_get_ints(struct interface_data *startint, 
			  struct interface_data *thisint, int *bufptr,
			  char *buffer, int bufsize, char *resbuf, 
			  int *resbufptr)
{
  struct ipc_cmd *cmd;
  struct interface_data *cur;
  int interfaces;
  char *retlist;
  struct ipc_header *header;

  if ((!startint) || (!thisint) || (!bufptr) || (!buffer) || (!resbuf) ||
      (!resbufptr))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_get_ints()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  if (cmd->attribute != INTERFACES) 
    {
      debug_printf(DEBUG_NORMAL, "Incorrect call to ipc_callout_get_ints!\n");
    }

  if (cmd->len != 0)
    {
      debug_printf(DEBUG_NORMAL, "Invalid length!  This will be the last request we answer!\n");
      *bufptr = bufsize;
    }
  *bufptr+=3;


  interfaces = 0;

  cur = startint;
  while (cur != NULL)
    {
      interfaces++;
      cur = cur->next;
    }

  retlist = (char *)malloc(interfaces*16);
  if (retlist == NULL) return;             // We won't answer.

  bzero(retlist, (interfaces*16));

  // Build the answer.
  cmd = (struct ipc_cmd *)&resbuf[*resbufptr];
  cmd->attribute = INTERFACES;

  *resbufptr += 2;
  
  cur = startint;
  while (cur != NULL)
    {
      strcat(retlist, cur->intName);
      cur = cur->next;
      if (cur != NULL) strcat(retlist, ",");
    }

  debug_printf(DEBUG_EVERYTHING, "Returning interface list of : %s\n", 
	       retlist);

  cmd->len = strlen(retlist);

  strncpy((char *)&resbuf[*resbufptr], retlist, strlen(retlist));
  *resbufptr += strlen(retlist);
  
  header->numcmds++;
}

/******************************************************************
 *
 * Build a message to be sent.  This should be used *ONLY* as a call
 * internal to the ipc_callout.c file!
 *
 ******************************************************************/
void ipc_callout_build_msg(struct interface_data *thisint, int *bufptr,
			   char *buffer, int bufsize, int msgtype, 
			   char *message)
{
  struct ipc_cmd *cmd;
  struct ipc_header *header;

  if ((!thisint) || (!bufptr) || (!buffer) || (!message))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_build_msg()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  cmd->attribute = msgtype;
  cmd->len = strlen(message);

  *bufptr += 2;

  strcpy((char *)&buffer[*bufptr], message);

  *bufptr += strlen(message);

  header->numcmds++;
}

/****************************************************************
 *
 * Send an error message to a client.
 *
 ****************************************************************/
void ipc_callout_send_error(struct interface_data *thisint, int *bufptr,
			    char *buffer, int bufsize, char *message)
{
  if ((!thisint) || (!bufptr) || (!buffer) || (!message))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_send_error()!\n");
      return;
    }

  ipc_callout_build_msg(thisint, bufptr, buffer, bufsize, ERROR_MSG, message);
}

/****************************************************************
 *
 * Send a notification to the client.
 *
 ****************************************************************/
void ipc_callout_send_notify(struct interface_data *thisint, int *bufptr,
			     char *buffer, int bufsize, char *message)
{
  if ((!thisint) || (!bufptr) || (!buffer) || (!message))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_send_notify()!\n");
      return;
    }

  ipc_callout_build_msg(thisint, bufptr, buffer, bufsize, NOTIFY, message);
}

/****************************************************************
 *
 * Get or set the profile we are using.
 *
 ****************************************************************/
void ipc_callout_getset_profile(struct interface_data *thisint, int *bufptr,
				char *buffer, int bufsize, char *resbuf, 
				int *resbufptr)
{
  struct ipc_header *header;
  //  struct ipc_cmd *cmd;

  if ((!thisint) || (!bufptr) || (!buffer) || (!resbuf) || (!resbufptr))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_getset_profile()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }
  debug_printf(DEBUG_NORMAL, "Get/Set Profile Not Implemented!\n");
}

/***************************************************************
 *
 * Set a temporary password.  This password will be used by the first EAP
 * method that needs it.  Once it has been used, the EAP method should
 * bzero, and free the memory, in order to keep the password from sitting
 * in memory too long.
 *
 ***************************************************************/
void ipc_callout_set_password(struct interface_data *thisint, int *bufptr,
			      char *buffer, int bufsize, char *resbuf, 
			      int *resbufptr)
{
  struct ipc_header *header;
  struct ipc_cmd *cmd;

  if ((!thisint) || (!bufptr) || (!buffer) || (!resbuf) || (!resbufptr))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_set_password()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  // If we already have a temp password, we need to get rid of it.
  if (thisint->tempPassword != NULL)
    {
      free(thisint->tempPassword);
      thisint->tempPassword = NULL;
    }
  
  thisint->tempPassword = (char *)malloc(cmd->len+1);
  if (thisint->tempPassword == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't allocate memory for temporary password!\n");
      return;
    }

  bzero(thisint->tempPassword, cmd->len+1);
  *bufptr += 2;
  strcpy(thisint->tempPassword, (char *)&buffer[*bufptr]);

  *bufptr += strlen(thisint->tempPassword);

  cmd = (struct ipc_cmd *)&resbuf[*resbufptr];

  cmd->attribute = TEMPPASSWORD;
  cmd->len = 1;
  
  *resbufptr += 2;

  resbuf[*resbufptr] = ACK;
  *resbufptr += 1;

  header->numcmds++;
}

/***********************************************************************
 *
 * Ask any attached clients for a password.  In this message, we will
 * also pass the EAP type that is requesting the password, and any
 * challenge string that the EAP type may need.
 *
 ***********************************************************************/
void ipc_callout_request_password(int *bufptr, char *buffer, int bufsize,
				  char *eapname, char *challenge)
{
  struct ipc_cmd *cmd;
  struct ipc_header *header;

  if ((!bufptr) || (!buffer) || (!eapname))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to ipc_callout_request_password()!\n");
      return;
    }

  header = (struct ipc_header *)buffer;

  // If the header is invalid, then return.
  if (header->version != 1)
    {
      debug_printf(DEBUG_NORMAL, "Invalid packet header.\n");
      return;
    }

  if (*bufptr <= 0)
    {
      *bufptr = sizeof(struct ipc_header);
    }

  cmd = (struct ipc_cmd *)&buffer[*bufptr];

  cmd->attribute = PASSWORD;
  if (challenge != NULL)
    {
      cmd->len = strlen(eapname)+strlen(challenge)+2;  // The string, with a NULL.
    } else {
      cmd->len = strlen(eapname)+2;
    }

  *bufptr += 2;

  bzero((char *)&buffer[*bufptr],cmd->len);
  strcpy((char *)&buffer[*bufptr], eapname);
  *bufptr += (strlen(eapname)+1);

  if (challenge != NULL)
    {
      strcpy((char *)&buffer[*bufptr], challenge);
      *bufptr += (strlen(challenge)+1);
    }

  header->numcmds++;
}
