/*
 * Secure OLSR plugin
 * http://www.olsr.org
 *
 * Copyright (c) 2004, Andreas Tønnesen(andreto@olsr.org)
 * All rights reserved.
 *
 * 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.
 * * Neither the name of olsrd, olsr.org 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.
 *
 * $Id: olsrd_secure.c,v 1.27 2007/08/02 14:37:09 bernd67 Exp $
 */


/*
 * Dynamic linked library for the olsr.org olsr daemon
 */

#include "olsrd_secure.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef linux
#include <linux/in_route.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <signal.h>

#include "defs.h"
#include "olsr.h"
#include "socket_parser.h"
#include "parser.h"
#include "scheduler.h"

#ifdef USE_OPENSSL
/* OpenSSL stuff */
#include <openssl/sha.h>

#define CHECKSUM SHA1
#define SCHEME   SHA1_INCLUDING_KEY

#else
/* Homebrewn checksuming */
#include "md5.h"

static void
MD5_checksum(const olsr_u8_t *data, const olsr_u16_t data_len, olsr_u8_t *hashbuf)
{
  MD5_CTX context;

  MD5Init(&context);
  MD5Update(&context, data, data_len);
  MD5Final(hashbuf, &context);
}

#define CHECKSUM MD5_checksum
#define SCHEME   MD5_INCLUDING_KEY

#endif

#ifdef OS
#undef OS
#endif

#ifdef WIN32
#define close(x) closesocket(x)
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#define OS "Windows"
#endif
#ifdef linux
#define OS "GNU/Linux"
#endif
#ifdef __FreeBSD__
#define OS "FreeBSD"
#endif

#ifndef OS
#define OS "Undefined"
#endif


/* Timestamp node */
struct stamp
{
  union olsr_ip_addr addr;
  /* Timestamp difference */
  int diff;
  olsr_u32_t challenge;
  olsr_u8_t validated;
  clock_t valtime; /* Validity time */
  clock_t conftime; /* Reconfiguration time */
  struct stamp *prev;
  struct stamp *next;
};

/* Seconds to cache a valid timestamp entry */
#define TIMESTAMP_HOLD_TIME 30
/* Seconds to cache a not verified timestamp entry */
#define EXCHANGE_HOLD_TIME 5

static struct stamp timestamps[HASHSIZE];


char keyfile[FILENAME_MAX+1];
char aes_key[16];
/* Input interface */
static struct interface *olsr_in_if;

/* Event function to register with the sceduler */
#if 0
static void olsr_event(void);
#endif
static int send_challenge(union olsr_ip_addr *);
static int ifchange(struct interface *, int);
static int send_cres(union olsr_ip_addr *, union olsr_ip_addr *, olsr_u32_t, struct stamp *);
static int send_rres(union olsr_ip_addr *, union olsr_ip_addr *, olsr_u32_t);
static int parse_challenge(char *);
static int parse_cres(char *);
static int parse_rres(char *);
static int check_auth(char *, int *);
#if 0
static int ipc_send(char *, int);
#endif
static int add_signature(olsr_u8_t *, int*);
static int validate_packet(char *, int*);
static void packet_parser(int);
static void timeout_timestamps(void*);
static int check_timestamp(union olsr_ip_addr *, time_t);
static struct stamp *lookup_timestamp_entry(union olsr_ip_addr *);
static int read_key_from_file(char *);

/**
 *Do initialization here
 *
 *This function is called by the my_init
 *function in uolsrd_plugin.c
 */

int
secure_plugin_init(void)
{
  struct interface *ints;
  int i;


  /* Initialize the timestamp database */
  for(i = 0; i < HASHSIZE; i++)
    {
      timestamps[i].next = &timestamps[i];
      timestamps[i].prev = &timestamps[i];
    }
  olsr_printf(1, "Timestamp database initialized\n");

  if(!strlen(keyfile))
    strcpy(keyfile, KEYFILE);

  i = read_key_from_file(keyfile);

  if(i < 0)
    {
      olsr_printf(1, "[ENC]Could not read key from file %s!\nExitting!\n\n", keyfile);
      exit(1);
    }
  if(i == 0)
    {
      olsr_printf(1, "[ENC]There was a problem reading key from file %s. Is the key long enough?\nExitting!\n\n", keyfile);
      exit(1);
    }

  /* Register the packet transform function */
  add_ptf(&add_signature);

  /* register ifchange function */
  add_ifchgf(&ifchange);

  /* Hijack OLSR socket parser functions */
  ints = ifnet;
  while(ints)
    {
      olsr_printf(1, "[ENC]Hijacking %s socket %d\n", ints->int_name, ints->olsr_socket);
      fflush(stdout);
      remove_olsr_socket(ints->olsr_socket, olsr_input);
      add_olsr_socket(ints->olsr_socket, &packet_parser);
     
      /* Reducing maxmessagesize */
      net_reserve_bufspace(ints, sizeof(struct s_olsrmsg));

      ints = ints->int_next;
    }

  /* Register timeout - poll every 2 seconds */
  olsr_register_scheduler_event(&timeout_timestamps, NULL, 2, 0 , NULL);

  return 1;
}

int
plugin_ipc_init(void)
{
  return 1;
}

/*
 * destructor - called at unload
 */
void
secure_plugin_exit(void)
{
}


#if 0
/**
 *Scheduled event
 */
static void
olsr_event(void)
{

}
#endif

#if 0
static int
ipc_send(char *data __attribute__((unused)), int size __attribute__((unused)))
{
  return 1;
}
#endif

/* XXX - ToDo */
static int
ifchange(struct interface *ifn, int action)
{

  switch(action)
    {
    case(IFCHG_IF_ADD):
      printf("SEC: interface %s added\n\n", ifn->int_name);
      olsr_printf(1, "[ENC]Hijacking %s socket %d\n", ifn->int_name, ifn->olsr_socket);
      remove_olsr_socket(ifn->olsr_socket, olsr_input);
      add_olsr_socket(ifn->olsr_socket, &packet_parser);
      /* Reducing maxmessagesize */
      net_reserve_bufspace(ifn, sizeof(struct s_olsrmsg));
      break;

    case(IFCHG_IF_REMOVE):
      printf("SEC: interface %s removed\n\n", ifn->int_name);
      olsr_printf(1, "[ENC]Removing %s socket %d\n", ifn->int_name, ifn->olsr_socket);
      remove_olsr_socket(ifn->olsr_socket, &packet_parser);
      break;

    case(IFCHG_IF_UPDATE):
      printf("SEC: interface %s updated\n\n", ifn->int_name);
      break;
      
    default:
      break;
    }
  return 0;
  
}


static void
packet_parser(int fd)
{
  /* sockaddr_in6 is bigger than sockaddr !!!! */
  struct sockaddr_storage from;
  socklen_t fromlen;
  int cc;
  union olsr_ip_addr from_addr;
  union
  {
    char	buf[MAXMESSAGESIZE+1];
    struct	olsr olsr;
  } inbuf;


  for (;;) 
    {
      fromlen = sizeof(struct sockaddr_storage);

      cc = recvfrom(fd, (void *)&inbuf, sizeof (inbuf), 0, (struct sockaddr *)&from, &fromlen);

      if (cc <= 0) 
	{
	  if (cc < 0 && errno != EWOULDBLOCK)
	    {
	      olsr_printf(1, "[ENC]error recvfrom: %s", strerror(errno));
	    }
	  break;
	}

      if(olsr_cnf->ip_version == AF_INET)
	{
	  /* IPv4 sender address */
	  memcpy(&from_addr, &((struct sockaddr_in *)&from)->sin_addr.s_addr, olsr_cnf->ipsize);
	}
      else
	{
	  /* IPv6 sender address */
	  memcpy(&from_addr, &((struct sockaddr_in6 *)&from)->sin6_addr, olsr_cnf->ipsize);
	}

      /*
      olsr_printf(1, "[ENC]message from %s size %d\n",
		  olsr_ip_to_string(&from_addr),
		  cc);
      */

      /* are we talking to ourselves? */
      if(if_ifwithaddr(&from_addr) != NULL)
      	return;


      /*
       *setting global from addr
       */
      //printf("Recieved data on socket %d\n", socknr);


      if((olsr_in_if = if_ifwithsock(fd)) == NULL)
	{
	  olsr_printf(1, "[ENC]Could not find input interface for message from %s size %d\n",
		      olsr_ip_to_string(&from_addr),
		      cc);
	  return ;
	}

      /*
       * Check for challenge/response messages
       */
      check_auth(inbuf.buf, &cc);

      /*
       * Check signature
       */

      if(!validate_packet(inbuf.buf, &cc))
	{
	  olsr_printf(1, "[ENC]Rejecting packet from %s\n", olsr_ip_to_string((union olsr_ip_addr *)&((struct sockaddr_in *)&from)->sin_addr.s_addr));
	  return;
	}


      olsr_printf(1, "[ENC]Packet from %s OK size %d\n", olsr_ip_to_string((union olsr_ip_addr *)&((struct sockaddr_in *)&from)->sin_addr.s_addr), cc);


      /* Fix OLSR packet header */
      inbuf.olsr.olsr_packlen = htons(cc);

      
      //olsr_printf(1, "Recieved a packet from %s\n", olsr_ip_to_string((union olsr_ip_addr *)&((struct sockaddr_in *)&from)->sin_addr.s_addr));

      //printf("\nCC: %d FROMLEN: %d\n\n", cc, fromlen);
      if ((olsr_cnf->ip_version == AF_INET) && (fromlen != sizeof (struct sockaddr_in)))
	break;
      else if ((olsr_cnf->ip_version == AF_INET6) && (fromlen != sizeof (struct sockaddr_in6)))
	break;


      /*
       * &from - sender
       * &inbuf.olsr 
       * cc - bytes read
       */
      parse_packet(&inbuf.olsr, cc, olsr_in_if, &from_addr);
    
    }
}



/**
 * Check a incoming OLSR packet for
 * challenge/responses.
 * They need not be verified as they
 * are signed in the message.
 *
 */
static int
check_auth(char *pck, int *size __attribute__((unused)))
{

  olsr_printf(3, "[ENC]Checking packet for challenge response message...\n");

  switch(pck[4])
    {
    case(TYPE_CHALLENGE):
      parse_challenge(&pck[4]);
      break;

    case(TYPE_CRESPONSE):
      parse_cres(&pck[4]);
      break;

    case(TYPE_RRESPONSE):
      parse_rres(&pck[4]);
      break;

    default:
      return 0;
    }

  return 1;
}



/**
 * Packet transform function
 * Build a SHA-1/MD5 hash of the original message
 * + the signature message(-digest) + key
 *
 * Then add the signature message to the packet and
 * increase the size
 */
int
add_signature(olsr_u8_t *pck, int *size)
{
  struct s_olsrmsg *msg;
#ifdef DEBUG
  int i, j;
  char *sigmsg;                                                                                        
#endif
  
  olsr_printf(2, "[ENC]Adding signature for packet size %d\n", *size);
  fflush(stdout);
  
  msg = (struct s_olsrmsg *)&pck[*size];
  /* Update size */
  ((struct olsr*)pck)->olsr_packlen = htons(*size + sizeof(struct s_olsrmsg));
  
  /* Fill packet header */
  msg->olsr_msgtype = MESSAGE_TYPE;
  msg->olsr_vtime = 0;
  msg->olsr_msgsize = htons(sizeof(struct s_olsrmsg));
  memcpy(&msg->originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
  msg->ttl = 1;
  msg->hopcnt = 0;
  msg->seqno = htons(get_msg_seqno());
  
  /* Fill subheader */
  msg->sig.type = ONE_CHECKSUM;
  msg->sig.algorithm = SCHEME;
  memset(&msg->sig.reserved, 0, 2);
  
  /* Add timestamp */
  msg->sig.timestamp = htonl(now.tv_sec);
  olsr_printf(3, "[ENC]timestamp: %ld\n", now.tv_sec);
  
  /* Set the new size */
  *size = *size + sizeof(struct s_olsrmsg);
  
  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, pck, *size - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[*size - SIGNATURE_SIZE], aes_key, KEYLENGTH);
  
  /* Create the hash */
  CHECKSUM(checksum_cache, (*size - SIGNATURE_SIZE) + KEYLENGTH, &pck[*size - SIGNATURE_SIZE]);
  } 

#ifdef DEBUG
  olsr_printf(1, "Signature message:\n");

  j = 0;
  sigmsg = (char *)msg;

  for(i = 0; i < sizeof(struct s_olsrmsg); i++)
    {
      olsr_printf(1, "  %3i", (u_char) sigmsg[i]);
      j++;
      if(j == 4)
	{
	  olsr_printf(1, "\n");
	  j = 0;
	}
    }
#endif

  olsr_printf(3, "[ENC] Message signed\n");

  return 1;
}



int
validate_packet(char *pck, int *size)
{
  int packetsize;
  olsr_u8_t sha1_hash[SIGNATURE_SIZE];
  struct s_olsrmsg *sig;
  time_t rec_time;

#ifdef DEBUG
  int i, j;
  char *sigmsg;
#endif

  /* Find size - signature message */
  packetsize = *size - sizeof(struct s_olsrmsg);

  if(packetsize < 4)
    return 0;

  sig = (struct s_olsrmsg *)&pck[packetsize];

  //olsr_printf(1, "Size: %d\n", packetsize);

#ifdef DEBUG
  olsr_printf(1, "Input message:\n");
  
  j = 0;
  sigmsg = (char *)sig;

  for(i = 0; i < sizeof(struct s_olsrmsg); i++)
    {
      olsr_printf(1, "  %3i", (u_char) sigmsg[i]);
      j++;
      if(j == 4)
	{
	  olsr_printf(1, "\n");
	  j = 0;
	}
    }
#endif

  /* Sanity check first */
  if((sig->olsr_msgtype != MESSAGE_TYPE) || 
     (sig->olsr_vtime) ||
     (sig->olsr_msgsize != ntohs(sizeof(struct s_olsrmsg))) ||
     (sig->ttl != 1) ||
     (sig->hopcnt != 0))
    {
      olsr_printf(1, "[ENC]Packet not sane!\n");
      return 0;
    }

  /* Check scheme and type */
  switch(sig->sig.type)
    {
    case(ONE_CHECKSUM):
      switch(sig->sig.algorithm)
	{
	case(SCHEME):
	  goto one_checksum_SHA; /* Ahhh... fix this */
	  break;
	  
	}
      break;

    default:
      olsr_printf(1, "[ENC]Unsupported sceme: %d enc: %d!\n", sig->sig.type, sig->sig.algorithm);
      return 0;
      break;

    }
  //olsr_printf(1, "Packet sane...\n");

 one_checksum_SHA:
 
 {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, pck, *size - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[*size - SIGNATURE_SIZE], aes_key, KEYLENGTH);

  /* generate SHA-1 */
  CHECKSUM(checksum_cache, *size - SIGNATURE_SIZE + KEYLENGTH, sha1_hash);
 }

#ifdef DEBUG
  olsr_printf(1, "Recevied hash:\n");
  
  sigmsg = (char *)sig->sig.signature;

  for(i = 0; i < SIGNATURE_SIZE; i++)
    {
      olsr_printf(1, " %3i", (u_char) sigmsg[i]);
    }
  olsr_printf(1, "\n");

  olsr_printf(1, "Calculated hash:\n");
  
  sigmsg = sha1_hash;

  for(i = 0; i < SIGNATURE_SIZE; i++)
    {
      olsr_printf(1, " %3i", (u_char) sigmsg[i]);
    }
  olsr_printf(1, "\n");
#endif

  if(memcmp(sha1_hash, sig->sig.signature, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Signature missmatch\n");
      return 0;
    }

  /* Check timestamp */
  rec_time = ntohl(sig->sig.timestamp);

  if(!check_timestamp((union olsr_ip_addr *)&sig->originator, rec_time))
    {
      olsr_printf(1, "[ENC]Timestamp missmatch in packet from %s!\n",
		  olsr_ip_to_string((union olsr_ip_addr *)&sig->originator));
      return 0;
    }

  olsr_printf(1, "[ENC]Received timestamp %ld diff: %ld\n", rec_time, now.tv_sec - rec_time);

  /* Remove signature message */
  *size = packetsize;
  return 1;
}


int
check_timestamp(union olsr_ip_addr *originator, time_t tstamp)
{
  struct stamp *entry;
  int diff;

  entry = lookup_timestamp_entry(originator);

  if(!entry)
    {
      /* Initiate timestamp negotiation */

      send_challenge(originator);

      return 0;
    }

  if(!entry->validated)
    {
      olsr_printf(1, "[ENC]Message from non-validated host!\n");
      return 0;
    }

  diff = entry->diff - (now.tv_sec - tstamp);

  olsr_printf(3, "[ENC]Timestamp slack: %d\n", diff);

  if((diff > UPPER_DIFF) || (diff < LOWER_DIFF))
    {
      olsr_printf(1, "[ENC]Timestamp scew detected!!\n");
      return 0;
    }

  /* ok - update diff */
  entry->diff = ((now.tv_sec - tstamp) + entry->diff) ?
    ((now.tv_sec - tstamp) + entry->diff) / 2 : 0;

  olsr_printf(3, "[ENC]Diff set to : %d\n", entry->diff);

  /* update validtime */

  entry->valtime = GET_TIMESTAMP(TIMESTAMP_HOLD_TIME * 1000);

  return 1;
}


/**
 * Create and send a timestamp
 * challenge message to new_host
 *
 * The host is registered in the timestamps
 * repository with valid=0
 */

int
send_challenge(union olsr_ip_addr *new_host)
{
  struct challengemsg cmsg;
  struct stamp *entry;
  olsr_u32_t challenge, hash;

  olsr_printf(1, "[ENC]Building CHALLENGE message\n");

  /* Set the size including OLSR packet size */


  challenge = rand() << 16;
  challenge |= rand();

  /* Fill challengemessage */
  cmsg.olsr_msgtype = TYPE_CHALLENGE;
  cmsg.olsr_vtime = 0;
  cmsg.olsr_msgsize = htons(sizeof(struct challengemsg));
  memcpy(&cmsg.originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
  cmsg.ttl = 1;
  cmsg.hopcnt = 0;
  cmsg.seqno = htons(get_msg_seqno());

  /* Fill subheader */
  memcpy(&cmsg.destination, new_host, olsr_cnf->ipsize);
  cmsg.challenge = htonl(challenge);

  olsr_printf(3, "[ENC]Size: %lu\n", (unsigned long)sizeof(struct challengemsg));

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, &cmsg, sizeof(struct challengemsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct challengemsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct challengemsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   cmsg.signature);
  }
  olsr_printf(3, "[ENC]Sending timestamp request to %s challenge 0x%x\n", 
	      olsr_ip_to_string(new_host),
	      challenge);

  /* Add to buffer */
  net_outbuffer_push(olsr_in_if, &cmsg, sizeof(struct challengemsg));

  /* Send the request */
  net_output(olsr_in_if);

  /* Create new entry */
  entry = malloc(sizeof(struct stamp));
  
  entry->diff = 0;
  entry->validated = 0;
  entry->challenge = challenge;

  memcpy(&entry->addr, new_host, olsr_cnf->ipsize);

  /* update validtime - not validated */
  entry->conftime = GET_TIMESTAMP(EXCHANGE_HOLD_TIME * 1000);

  hash = olsr_hashing(new_host);
  
  /* Queue */
  timestamps[hash].next->prev = entry;
  entry->next = timestamps[hash].next;
  timestamps[hash].next = entry;
  entry->prev = &timestamps[hash];


  return 1;

}

int
parse_cres(char *in_msg)
{
  struct c_respmsg *msg;
  olsr_u8_t sha1_hash[SIGNATURE_SIZE];
  struct stamp *entry;

  msg = (struct c_respmsg *)in_msg;

  olsr_printf(1, "[ENC]Challenge-response message received\n");
  olsr_printf(3, "[ENC]To: %s\n", olsr_ip_to_string((union olsr_ip_addr *)&msg->destination));

  if(if_ifwithaddr((union olsr_ip_addr *)&msg->destination) == NULL)
    {
      olsr_printf(3, "[ENC]Not for us...\n");
      return 0;
    }

  olsr_printf(3, "[ENC]Challenge: 0x%lx\n", (unsigned long)ntohl(msg->challenge)); /* ntohl() returns a unsignedlong onwin32 */

  /* Check signature */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, msg, sizeof(struct c_respmsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct c_respmsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);
  
  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct c_respmsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   sha1_hash);
  }
  
  if(memcmp(sha1_hash, &msg->signature, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Signature missmatch in challenge-response!\n");
      return 0;
    }

  olsr_printf(3, "[ENC]Signature verified\n");


  /* Now to check the digest from the emitted challenge */
  if((entry = lookup_timestamp_entry((union olsr_ip_addr *)&msg->originator)) == NULL)
    {
      olsr_printf(1, "[ENC]Received challenge-response from non-registered node %s!\n",
		  olsr_ip_to_string((union olsr_ip_addr *)&msg->originator));
      return 0;
    }

  /* Generate the digest */
  olsr_printf(3, "[ENC]Entry-challenge 0x%x\n", entry->challenge);

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* First the challenge received */
  memcpy(checksum_cache, &entry->challenge, 4);
  /* Then the local IP */
  memcpy(&checksum_cache[sizeof(olsr_u32_t)], &msg->originator, olsr_cnf->ipsize);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   sizeof(olsr_u32_t) + olsr_cnf->ipsize, 
	   sha1_hash);
  }

  if(memcmp(msg->res_sig, sha1_hash, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Error in challenge signature from %s!\n",
		  olsr_ip_to_string((union olsr_ip_addr *)&msg->originator));
      
      return 0;
    }

  olsr_printf(3, "[ENC]Challenge-response signature ok\n");

  /* Update entry! */


  entry->challenge = 0;
  entry->validated = 1;
  entry->diff = now.tv_sec - msg->timestamp;

  /* update validtime - validated entry */
  entry->valtime = GET_TIMESTAMP(TIMESTAMP_HOLD_TIME * 1000);

  olsr_printf(1, "[ENC]%s registered with diff %d!\n",
	      olsr_ip_to_string((union olsr_ip_addr *)&msg->originator),
	      entry->diff);

  /* Send response-response */
  send_rres((union olsr_ip_addr *)&msg->originator, 
	    (union olsr_ip_addr *)&msg->destination, 
	    ntohl(msg->challenge));

  return 1;
}


int
parse_rres(char *in_msg)
{
  struct r_respmsg *msg;
  olsr_u8_t sha1_hash[SIGNATURE_SIZE];
  struct stamp *entry;

  msg = (struct r_respmsg *)in_msg;

  olsr_printf(1, "[ENC]Response-response message received\n");
  olsr_printf(3, "[ENC]To: %s\n", olsr_ip_to_string((union olsr_ip_addr *)&msg->destination));

  if(if_ifwithaddr((union olsr_ip_addr *)&msg->destination) == NULL)
    {
      olsr_printf(1, "[ENC]Not for us...\n");
      return 0;
    }

  /* Check signature */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, msg, sizeof(struct r_respmsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct r_respmsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);
  
  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct r_respmsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   sha1_hash);
  }
  
  if(memcmp(sha1_hash, &msg->signature, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Signature missmatch in response-response!\n");
      return 0;
    }

  olsr_printf(3, "[ENC]Signature verified\n");


  /* Now to check the digest from the emitted challenge */
  if((entry = lookup_timestamp_entry((union olsr_ip_addr *)&msg->originator)) == NULL)
    {
      olsr_printf(1, "[ENC]Received response-response from non-registered node %s!\n",
		  olsr_ip_to_string((union olsr_ip_addr *)&msg->originator));
      return 0;
    }

  /* Generate the digest */
  olsr_printf(3, "[ENC]Entry-challenge 0x%x\n", entry->challenge);

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* First the challenge received */
  memcpy(checksum_cache, &entry->challenge, 4);
  /* Then the local IP */
  memcpy(&checksum_cache[sizeof(olsr_u32_t)], &msg->originator, olsr_cnf->ipsize);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   sizeof(olsr_u32_t) + olsr_cnf->ipsize, 
	   sha1_hash);
  }

  if(memcmp(msg->res_sig, sha1_hash, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Error in response signature from %s!\n",
		  olsr_ip_to_string((union olsr_ip_addr *)&msg->originator));
      
      return 0;
    }

  olsr_printf(3, "[ENC]Challenge-response signature ok\n");

  /* Update entry! */


  entry->challenge = 0;
  entry->validated = 1;
  entry->diff = now.tv_sec - msg->timestamp;

  /* update validtime - validated entry */
  entry->valtime = GET_TIMESTAMP(TIMESTAMP_HOLD_TIME * 1000);

  olsr_printf(1, "[ENC]%s registered with diff %d!\n",
	      olsr_ip_to_string((union olsr_ip_addr *)&msg->originator),
	      entry->diff);

  return 1;
}


int
parse_challenge(char *in_msg)
{
  struct challengemsg *msg;
  olsr_u8_t sha1_hash[SIGNATURE_SIZE];
  struct stamp *entry;
  olsr_u32_t hash;

  msg = (struct challengemsg *)in_msg;

  olsr_printf(1, "[ENC]Challenge message received\n");
  olsr_printf(3, "[ENC]To: %s\n", olsr_ip_to_string((union olsr_ip_addr *)&msg->destination));

  if(if_ifwithaddr((union olsr_ip_addr *)&msg->destination) == NULL)
    {
      olsr_printf(1, "[ENC]Not for us...\n");
      return 0;
    }

  /* Create entry if not registered */
  if((entry = lookup_timestamp_entry((union olsr_ip_addr *)&msg->originator)) == NULL)
    {
      entry = malloc(sizeof(struct stamp));
      memcpy(&entry->addr, &msg->originator, olsr_cnf->ipsize);

      hash = olsr_hashing((union olsr_ip_addr *)&msg->originator);
  
      /* Queue */
      timestamps[hash].next->prev = entry;
      entry->next = timestamps[hash].next;
      timestamps[hash].next = entry;
      entry->prev = &timestamps[hash];
    }
  else
    {
      /* Check configuration timeout */
      if(!TIMED_OUT(entry->conftime))
	{
	  /* If registered - do not accept! */
	  olsr_printf(1, "[ENC]Challenge from registered node...dropping!\n");
	  return 0;
	}
      else
	{
	  olsr_printf(1, "[ENC]Challenge from registered node...accepted!\n");
	}
    }

  olsr_printf(3, "[ENC]Challenge: 0x%lx\n", (unsigned long)ntohl(msg->challenge)); /* ntohl() returns a unsignedlong onwin32 */

  /* Check signature */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, msg, sizeof(struct challengemsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct challengemsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);
  
  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct challengemsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   sha1_hash);
  }  
  if(memcmp(sha1_hash, &msg->signature, SIGNATURE_SIZE) != 0)
    {
      olsr_printf(1, "[ENC]Signature missmatch in challenge!\n");
      return 0;
    }

  olsr_printf(3, "[ENC]Signature verified\n");

  
  entry->diff = 0;
  entry->validated = 0;

  /* update validtime - not validated */
  entry->conftime = GET_TIMESTAMP(EXCHANGE_HOLD_TIME * 1000);

  /* Build and send response */

  send_cres((union olsr_ip_addr *)&msg->originator, 
	    (union olsr_ip_addr *)&msg->destination, 
	    ntohl(msg->challenge),
	    entry);

  return 1;
}





/**
 * Build and transmit a challenge response
 * message.
 *
 */
int
send_cres(union olsr_ip_addr *to, union olsr_ip_addr *from, olsr_u32_t chal_in, struct stamp *entry)
{
  struct c_respmsg crmsg;
  olsr_u32_t challenge;

  olsr_printf(1, "[ENC]Building CRESPONSE message\n");

  challenge = rand() << 16;
  challenge |= rand();

  entry->challenge = challenge;

  olsr_printf(3, "[ENC]Challenge-response: 0x%x\n", challenge);

  /* Fill challengemessage */
  crmsg.olsr_msgtype = TYPE_CRESPONSE;
  crmsg.olsr_vtime = 0;
  crmsg.olsr_msgsize = htons(sizeof(struct c_respmsg));
  memcpy(&crmsg.originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
  crmsg.ttl = 1;
  crmsg.hopcnt = 0;
  crmsg.seqno = htons(get_msg_seqno());

  /* set timestamp */
  crmsg.timestamp = now.tv_sec;
  olsr_printf(3, "[ENC]Timestamp %ld\n", crmsg.timestamp);

  /* Fill subheader */
  memcpy(&crmsg.destination, to, olsr_cnf->ipsize);
  crmsg.challenge = htonl(challenge);

  /* Create digest of received challenge + IP */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the challenge received */
  memcpy(checksum_cache, &chal_in, 4);
  /* Then the local IP */
  memcpy(&checksum_cache[sizeof(olsr_u32_t)], from, olsr_cnf->ipsize);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   sizeof(olsr_u32_t) + olsr_cnf->ipsize, 
	   crmsg.res_sig);
  }

  /* Now create the digest of the message and the key */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, &crmsg, sizeof(struct c_respmsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct c_respmsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct c_respmsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   crmsg.signature);
  }

  olsr_printf(3, "[ENC]Sending challenge response to %s challenge 0x%x\n", 
	      olsr_ip_to_string(to),
	      challenge);

  /* Add to buffer */
  net_outbuffer_push(olsr_in_if, &crmsg, sizeof(struct c_respmsg));
  /* Send the request */
  net_output(olsr_in_if);

  return 1;
}






/**
 * Build and transmit a response response
 * message.
 *
 */
static int
send_rres(union olsr_ip_addr *to, union olsr_ip_addr *from, olsr_u32_t chal_in)
{
  struct r_respmsg rrmsg;

  olsr_printf(1, "[ENC]Building RRESPONSE message\n");


  /* Fill challengemessage */
  rrmsg.olsr_msgtype = TYPE_RRESPONSE;
  rrmsg.olsr_vtime = 0;
  rrmsg.olsr_msgsize = htons(sizeof(struct r_respmsg));
  memcpy(&rrmsg.originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
  rrmsg.ttl = 1;
  rrmsg.hopcnt = 0;
  rrmsg.seqno = htons(get_msg_seqno());

  /* set timestamp */
  rrmsg.timestamp = now.tv_sec;
  olsr_printf(3, "[ENC]Timestamp %ld\n", rrmsg.timestamp);

  /* Fill subheader */
  memcpy(&rrmsg.destination, to, olsr_cnf->ipsize);

  /* Create digest of received challenge + IP */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the challenge received */
  memcpy(checksum_cache, &chal_in, 4);
  /* Then the local IP */
  memcpy(&checksum_cache[sizeof(olsr_u32_t)], from, olsr_cnf->ipsize);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   sizeof(olsr_u32_t) + olsr_cnf->ipsize, 
	   rrmsg.res_sig);
  }

  /* Now create the digest of the message and the key */

  {
  olsr_u8_t checksum_cache[512 + KEYLENGTH];
  /* Create packet + key cache */
  /* First the OLSR packet + signature message - digest */
  memcpy(checksum_cache, &rrmsg, sizeof(struct r_respmsg) - SIGNATURE_SIZE);
  /* Then the key */
  memcpy(&checksum_cache[sizeof(struct r_respmsg) - SIGNATURE_SIZE], aes_key, KEYLENGTH);

  /* Create the hash */
  CHECKSUM(checksum_cache, 
	   (sizeof(struct r_respmsg) - SIGNATURE_SIZE) + KEYLENGTH, 
	   rrmsg.signature);
  }

  olsr_printf(3, "[ENC]Sending response response to %s\n", 
	      olsr_ip_to_string(to));

  /* add to buffer */
  net_outbuffer_push(olsr_in_if, &rrmsg, sizeof(struct r_respmsg));

  /* Send the request */
  net_output(olsr_in_if);

  return 1;
}



static struct stamp *
lookup_timestamp_entry(union olsr_ip_addr *adr)
{
  olsr_u32_t hash;
  struct stamp *entry;

  hash = olsr_hashing(adr);

  for(entry = timestamps[hash].next;
      entry != &timestamps[hash];
      entry = entry->next)
    {
      if(memcmp(&entry->addr, adr, olsr_cnf->ipsize) == 0)
	{
	  olsr_printf(3, "[ENC]Match for %s\n", olsr_ip_to_string(adr));
	  return entry;
	}
    }

  olsr_printf(1, "[ENC]No match for %s\n", olsr_ip_to_string(adr));

  return NULL;
}



/**
 *Find timed out entries and delete them
 *
 *@return nada
 */
void
timeout_timestamps(void* foo __attribute__((unused)))
{
  struct stamp *tmp_list;
  struct stamp *entry_to_delete;
  int index;


  for(index=0;index<HASHSIZE;index++)
    {
      tmp_list = timestamps[index].next;
      /*Traverse MID list*/
      while(tmp_list != &timestamps[index])
	{
	  /*Check if the entry is timed out*/
	  if((TIMED_OUT(tmp_list->valtime)) && (TIMED_OUT(tmp_list->conftime)))
	    {
	      entry_to_delete = tmp_list;
	      tmp_list = tmp_list->next;

	      olsr_printf(1, "[ENC]timestamp info for %s timed out.. deleting it\n", 
			  olsr_ip_to_string(&entry_to_delete->addr));

	      /*Delete it*/
	      entry_to_delete->next->prev = entry_to_delete->prev;
	      entry_to_delete->prev->next = entry_to_delete->next;

	      free(entry_to_delete);
	    }
	  else
	      tmp_list = tmp_list->next;
	}
    }

  return;
}



static int
read_key_from_file(char *file)
{
  FILE *kf;
  size_t keylen;

  keylen = 16;
  kf = fopen(file, "r");

  olsr_printf(1, "[ENC]Reading key from file \"%s\"\n", file);

  if(kf == NULL)
    {
      olsr_printf(1, "[ENC]Could not open keyfile %s!\nError: %s\n", file, strerror(errno));
      return -1;
    }

  if(fread(aes_key, 1, keylen, kf) != keylen)
    {
      olsr_printf(1, "[ENC]Could not read key from keyfile %s!\nError: %s\n", file, strerror(errno));
      fclose(kf);
      return 0;
    }


  fclose(kf);
  return 1;
}



syntax highlighted by Code2HTML, v. 0.9.1