/*
 * 
 * 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 timestamp request"
#define DESCRIPTION "\
This plugin performs an ICMP_TIMESTAMP request,\n\
and may obtain the remote host time, which may be\n\
used by an attacker when attacking time based \n\
authentication protocols.\n\
Risk factor : low"

#define COPYRIGHT "licensed under the GPL"
#define SUMMARY "sends an ICMP_TIMESTAMP request"


#ifndef ICMP_TSTAMP 
#define ICMP_TSTAMP 13
#endif

#ifndef ICMP_TSTAMPREPLY 
#define ICMP_TSTAMPREPLY 14
#endif

#ifndef ICMP_TSLEN
#define ICMP_TSLEN      (8 + 3 * sizeof (n_time)) 
#endif
int icmp_timestamp(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(icmp_timestamp(env))
   {
    proto_post_info(env, 0, "icmp", 
 "The remote hosts answered to an icmp TIMESTAMP request.\n\
This will give away the remote host current time to an\n\
attacker, and this may help him to bypass time based \n\
authentification protocols\n\
Solution : configure your firewalls/router and deny ICMP TIMESTAMP requests");
   }
}

/*
 * 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 icmp_timestamp(struct arglist *  env)
{
 int soc;
 struct in_addr addr, *p_addr;
 char * buf;
 struct ip *ip;
 struct icmp *icmp;
 struct protoent * ip_proto;
 int len;
 struct timeval tv = {0,0};
 struct sockaddr_in saddr;
 unsigned char * c_addr;
 int count = 1;
 int max_to = 5;
 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)
 {
  int i;
  fd_set r,w;
  int n;

  buf = (char*)malloc(ICMP_TSLEN);
  bzero(buf, ICMP_TSLEN);
  icmp = (struct icmp *)buf;
  icmp->icmp_type = ICMP_TSTAMP;
  icmp->icmp_code = 0;
  icmp->icmp_cksum = 0;
  icmp->icmp_seq = 1;
  icmp->icmp_id = getpid() & 0xFFFF;
  icmp->icmp_otime = time(NULL);
  len = ICMP_TSLEN;
  saddr.sin_family = AF_INET;
  saddr.sin_addr = addr;
  saddr.sin_port = 0;
  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);
  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_TSTAMPREPLY)&&
      (icmp->icmp_id == (getpid() & 0xFFFF)))ok = 1;
   free(buf);
   }
   else max_to--;
  }
  shutdown(soc, 2);
  close(soc);
  return(ok);
}
  

