/*
 * 
 * Firewall icmp.c
 *
 * This plugin attempts to send an icmp ping request to a host
 * which is supposed to be firewall-protected, and warns the user
 * if it gets a reply
 *
 * This plugin is (c) Renaud Deraison and is licensed under the GPL
 * 
 * The biggest part of this plugin has been cut'n'pasted from the hostloop library,
 * which happens to be written by me
 *
 */
 
#include <includes.h>
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#include "icmp.h"

#define NAME "firewall icmp check"
#define DESCRIPTION "\
Some misconfigured firewalls let icmp packets \n\
go through the network. This is a problem since \n\
the \"protected\" hosts may be subject to denial of \n\
service attacks (like the ping of death), and this \n\
may allow an intruder to insert some data inside \n\
the network, which is mostly what you want to avoid.\n\
Last but not least, it may allow an intruder to cut the\n\
connection between two hosts (sending false ICMP_UNREACH\n\
messages)\n\n\
Risk factor : medium/high"

#define COPYRIGHT "licensed under the GPL"
#define SUMMARY "attempts to insert icmp packets in a firewall-protected network"

int ping_host(struct arglist *  env);

PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
  plug_set_name(desc, NAME);
  plug_set_description(desc, DESCRIPTION);
  plug_set_summary(desc, SUMMARY);
  plug_set_copyright(desc, COPYRIGHT);
  plug_set_category(desc, ACT_GATHER_INFO);
  plug_set_family(desc, "Firewalls");
  return(0);
}


PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
 char * outside = get_preference(env, "outside_firewall");
 if(outside && !strcmp(outside, "yes"))
 {
  if(ping_host(env))
   {
    proto_post_hole(env, 0, "icmp", 
 "The remote host answered to an icmp ECHO query, even though\n\
it's protected by a firewall, which is not a good thing\n\n\
Solution : reconfigure your firewall and deny icmp traffic from\n\
the outside");
   }
 }
}

/*
 * Checksum routine for Internet Protocol family headers (C Version)
 * From ping examples in W.Richard Stevens "UNIX NETWORK PROGRAMMING" book.
 */
int in_cksum(p,n)
u_short *p; int n;
{
  register u_short answer;
  register long sum = 0;
  u_short odd_byte = 0;

  while( n > 1 )  { sum += *p++; n -= 2; }

  /* mop up an odd byte, if necessary */
  if( n == 1 ) {
      *(u_char *)(&odd_byte) = *(u_char *)p;
      sum += odd_byte;
  }

  sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
  sum += (sum >> 16);			/* add carry */
  answer = (int)~sum;			/* ones-complement, truncate*/
  return (answer);
}

int ping_host(struct arglist *  env)
{
 int soc;
 struct in_addr addr, *p_addr;
 char * buf;
 struct ip *ip;
 struct icmp *icmp;
 struct protoent * ip_proto;
 struct timeval tv;
#ifndef NESSUSNT
 struct timezone tz;
#endif
 int len;
 struct sockaddr_in saddr;
 int count = 1;
 int max_to = 3;
 int ok = 0;
 
 bzero(&saddr, sizeof(struct sockaddr_in));
 p_addr = plug_get_host_ip(env);
 addr = *p_addr;
 
 ip_proto = getprotobyname("icmp");
 if(!ip_proto)return(1);
  
 soc = socket(AF_INET, SOCK_RAW, ip_proto->p_proto);
 if(soc < 0 )return(1);
 while(max_to && !ok)
 {
  fd_set r,w;
  int n;
  buf = (char*)malloc(32);
  bzero(buf, 32);
  icmp = (struct icmp *)buf;
#ifndef NESSUSNT
  gettimeofday(&tv, &tz);
#endif
  icmp->icmp_type = ICMP_ECHO;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = 0;
  icmp->icmp_seq = 1;
 /* SET_ICMP_LIFETIME(*icmp, 1024);*/
  icmp->icmp_id = getpid() & 0xFFFF;

#ifndef NESSUSNT
  bcopy(&tv, &buf[8], sizeof(struct timeval));
  len = 8 + sizeof(struct timeval)+sizeof(int);
#else
  len = 8 + sizeof(int);
#endif
  saddr.sin_family = AF_INET;
  saddr.sin_addr = addr;
  saddr.sin_port = 0;
  bcopy(&count, &buf[8+sizeof(struct timeval)], sizeof(int));
  icmp->icmp_cksum = in_cksum((u_short *)icmp,len);
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  
  free(buf);
  
  FD_ZERO(&r);
  FD_ZERO(&w);
#ifndef NESSUSNT  
  tv.tv_sec = 3;
  tv.tv_usec = 0;
#endif
  FD_SET(soc, &r);
  n = select(soc+1, &r, &w, NULL, &tv);
  if(n>0)
  {
   int slen = sizeof(struct sockaddr);
   buf = malloc(4096);
   bzero(buf, 4096);
   ip  = (struct ip *)buf;
   bzero(&saddr, sizeof(struct sockaddr_in));
   recvfrom(soc, buf, 4096, 0, (struct sockaddr *)&saddr, &slen);
   icmp = (struct icmp *)(buf + (ip->ip_hl << 2));
   if((icmp->icmp_type == ICMP_ECHOREPLY)&&
      (icmp->icmp_id == (getpid() & 0xFFFF)))
	  ok = 1;
   free(buf);
   }
   else max_to--;
  }
  shutdown(soc, 2);
  close(soc);
  return(ok);
}
  

