/*
 * 
 * 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 "icmp broadcast check"
#define DESCRIPTION "\
Routers should not allow broadcasted icmp\n\
ping packets to go through the network\n\
Allowing such packets is threat, because\n\
it means that the network may be vulnerable\n\
to the smurf attack\n\
Risk factor : medium/high"

#define COPYRIGHT "licensed under the GPL"
#define SUMMARY "sends broadcasted icmp packets"

int ping_subnet(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, "Misc.");
  return(0);
}


PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
  if(ping_subnet(env))
   {
    proto_post_hole(env, 0, "icmp", 
 "The remote hosts answered to a broadcasted icmp ECHO query\n\
This means that the remote network may be subject to the\n\
'smurf' attack\n\
Solution : configure your routers and deny broadcasted ICMP packets");
   }
}

/*
 * 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_subnet(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;
 unsigned char * c_addr;
 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;
 c_addr = (unsigned char *)&(addr.s_addr);
 c_addr[3]=255;
 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));
  sendto(soc, buf, len, 0, (struct sockaddr *)(&saddr), sizeof(struct sockaddr_in));
  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);
}
  

