/*Spike_dce_rpc.c*/
/*Dave Aitel, 2001*/
/*License: GPL v2.0 */
/*this supports msrpcfuzz, and provides a few useful dce-rpc functions
in case someone else wants them*/
/*this will even do ltlm authentication - eventually*/
/*TODO

  Fix the string stuff so it's a real DCE string.
  Get multi-fragment queries working better

*/

#include "spike.h"
#include "spike_dcerpc.h"


#include <string.h> /*for memset, memcpy, etc*/
#include <malloc.h> /*for malloc()*/
#include <stdlib.h> /*for rand()*/
#include <unistd.h>/*for read()*/




char *
string_from_buffer(unsigned char *buf,int length)
{
  unsigned char *p;
  unsigned char * retstring;
  int i;
  static char * memleak=NULL;

  retstring=malloc(length*2+1);
  /*lame, I know*/
  if (memleak!=NULL)
	  {free(memleak);}
  memleak=retstring;

  memset(retstring,0x00,length*2+1);

  p=buf;
  for (i=0; i<length; i++,p++)
    {
      sprintf(retstring+i*2,"%2.2x",*p);
    }

  /*oooh, the memory leak is nice*/
  return retstring;
}

unsigned char
hex_nib_from_hbyte(unsigned char byte)
{
  int c;
  int b;
  c=byte;
  if ((c >= '0') && (c <= '9'))
    b=c-'0';
  else if ((c >= 'a') && (c <= 'f'))
    b=c-'a'+10;
  else if ((c >= 'A') && (c <= 'F'))
    b=c-'A'+10;

  return b;
}

int
hexstring_to_buffer(char * hexstring,char * buffer)
{
  int i;

  /*
  printf("hexstring_to_buffer %d = %s\n",strlen(hexstring),hexstring);
  */
  /*for each byte of the buffer*/
  for (i=0; i<strlen(hexstring)/2; i++)
    {
      unsigned char nib1;
      unsigned char nib2;
      /*debug hex crap*/
      /*printf("hexstring[i*2]=%c i*2+1=%c\n",hexstring[i*2],hexstring[i*2+1]);*/
      nib1=hex_nib_from_hbyte(hexstring[i*2]);
      nib2=hex_nib_from_hbyte(hexstring[i*2+1]);
      buffer[i]=(nib1<<4)|(nib2<<0);
      /*debug hex crap*/
      /*
      printf("nib1=%x, nib2=%x buffer[i]=%2x\n",nib1,nib2,buffer[i]);
      */
    }
 
  return i;
}

/*takes it in raw off the wire, in wierd intel order*/
/*always 16 bytes long*/
int guid_rawbuffer_to_string(char * buffer, char * mystring)
{
  /*16 total*/
  char tbuffer[4];
  char tbuffer2[2];
  char tbuffer3[2];
  char tbuffer4[2];
  char tbuffer5[6];

  memcpy(tbuffer,buffer,4); /*don't mess with original buffer*/
  memcpy(tbuffer2,buffer+4,2); /*don't mess with original buffer*/
  memcpy(tbuffer3,buffer+6,2); /*don't mess with original buffer*/
  memcpy(tbuffer4,buffer+8,2); /*don't mess with original buffer*/
  memcpy(tbuffer5,buffer+10,6); /*don't mess with original buffer*/
  
  /*swap the bytes that need to be swapped*/
  intel_order(tbuffer,4);
  intel_order(tbuffer2,2);
  intel_order(tbuffer3,2);
  sprintf(mystring,"%s-%s-%s-%s-%s",
	  string_from_buffer(tbuffer,sizeof(tbuffer)),
	  string_from_buffer(tbuffer2,sizeof(tbuffer2)),
	  string_from_buffer(tbuffer3,sizeof(tbuffer3)),
	  string_from_buffer(tbuffer4,sizeof(tbuffer4)),
	  string_from_buffer(tbuffer5,sizeof(tbuffer5)));
  
  return 1; /*always works because we're so cool*/
}


/*hmm. we need to do the intel-reversing on this thing*/
int
uuid_string_to_buffer(unsigned char * buffer, char *uuid)
{
  char tempbuffer[400];
  int err;

  memset(tempbuffer,0x00,sizeof(tempbuffer));
  /*uuids look like <4bytes>-<2bytes>-<2bytes>-<2bytes>-<6bytes>*/
  /*all this stuff is just to get rid of the dashes. Sucks, but sue me.*/
  memcpy(tempbuffer,uuid,8);
  memcpy(tempbuffer+8,uuid+9,4);
  memcpy(tempbuffer+12,uuid+14,4);
  memcpy(tempbuffer+16,uuid+19,4);
  memcpy(tempbuffer+20,uuid+24,12);

  /*  printf("uuid_string_to_buffer:%s from %s\n",tempbuffer,uuid);*/
  err=hexstring_to_buffer(tempbuffer,buffer);
  if (err)
    {
      intel_order(buffer,4);
      intel_order(buffer+4,2);
      intel_order(buffer+6,2);
      /*intel_order(buffer+8,2);*/ /*this doesn't happen eithar for some reason*/
      /*for some reason the last 8 bytes are in network byte order.*/
      /*printf("returning %d\n",err);*/
      return err;
    }
  else
    {
      return 0;
    }
}


/*returns 1 on success, 0 on failure*/
/*I think callid is actually ignored by MSRPC servers. Pass something in though.*/
int
s_dce_bind(char * uuid,char * transfer_syntax,
	   unsigned int callid, unsigned int interfaceversionmajor, unsigned int interfaceversionminor,
	   unsigned int syntaxversion)
{

  unsigned char uuidbuffer[400]; 
  unsigned int uuidlength;


  /*why does the DCEFrag start here? - but it does*/
  s_block_start("DCEFragLength");  
  /*version*/
  s_binary("05");

  /*minor version*/
  s_binary("00");
  
  s_binary(BINDPACKET);
  /*binds are always just one packet*/
  /*  s_binary((unsigned char) 0x00 | LASTFRAG | FIRSTFRAG);*/
  s_binary("0x03");



  /*now set Data Rep, whatever that is */
  s_intelword(0x00000010);

  /*now we have 2 bytes of frag length*/
  s_binary_block_size_intel_halfword("DCEFragLength");

  /*auth length*/
  s_binary_block_size_intel_halfword("DCEAuthLength");


  /*call ID */
  s_intelword(callid);

  /*buffer xmit frag*/
  s_binary("d016");
  /*  s_binary("d016");*/

  /*buffer max recv frag*/
  s_binary("d016");
  /*  s_binary("d016");*/

  /*assoc Group*/
  s_binary("00000000");
  /*s_binary("00000000");*/

  /*num ctx items - ethereal incorrect reports this as 1 byte*/
  s_intelword(1);
  
  /*context ID*/
  s_binary("0x0000");

  /*num trans items*/
  /*0x0100*/
  s_intelhalfword(0x01);


  /*Interface UUID*/
  uuidlength=uuid_string_to_buffer(uuidbuffer,uuid);
  s_push(uuidbuffer,uuidlength);

  /*this is actually 2 intel-ordered shorts, a major and then a minor version*/
  /*0x3000 0000*/ /*major of 3, minor of zero*/
  s_intelhalfword(interfaceversionmajor);
  s_intelhalfword(interfaceversionminor);

  /*now do syntax*/
  uuidlength=uuid_string_to_buffer(uuidbuffer,transfer_syntax);
  s_push(uuidbuffer,uuidlength);
  
  /*0x200000000*/
  s_intelword(syntaxversion);

  s_block_start("DCEAuthLength");
  s_block_end("DCEAuthLength");
  /*caller must end this explicitly*/

  s_block_end("DCEFragLength");  

  return 1;
}

/*I have no idea which it is!*/
/*actually, according to Luke kennith Casington, it's neithar.
But we'll fudge it because it seems to work until we have
time to fix it*/
int 
s_dce_wordstring(unsigned char * mystring)
{
 unsigned int size;
  unsigned int size2;
  unsigned int i;
  
  size=strlen(mystring);
  if (size > (unsigned int)0xffffffff-(unsigned int)12)
    printf("Warning, dce_wordstring() length overflowed!\n");
  size2=size;
 /*page 51 of DCE-RPC book*/ 
  s_intelword(size2);
  s_intelword(0);
  s_intelword(size2);
  for (i=0; i< size2; i++)
    s_push(mystring+i,1);

  while (size % 4 != 0)
    {
      s_binary("00");
      size++;
    }

  return size+12; /*2 for intel word */
}

int 
s_dce_string(unsigned char * mystring)
{
  unsigned short size;
  unsigned short size2;
  unsigned int i;
  
  size=strlen(mystring);
  size2=size;
  
  s_intelhalfword(size2);
  for (i=0; i< size2; i++)
    s_push(mystring+i,1);

  while (size % 4 != 0)
    {
      s_binary("00");
      size++;
    }

  return size+2; /*2 for intel half word */
}

int
s_random_dce_string()
{

  /*pick a random length properly*/

  //just in case
  s_init_fuzzing();

#define TOPLENGTH 0xffff
  //top=s_get_max_fuzzstring();
  
  //picked=1+(unsigned short) (top*1.0 *rand()/(RAND_MAX+1.0));

  //length=1+(unsigned short) (top*1.0 *rand()/(RAND_MAX+1.0));

  //randstring=malloc(length+1);
  //memset(randstring,0x41,length);
  //randstring[length]=0;

  /*not sure which one is correct!*/
  s_dce_wordstring(s_get_random_fuzzstring());
  /*s_dce_wordstring(randstring);*/


  //free(randstring);
  return 1;
}

/*requires caller to also use s_block_end("DCEFragLength")*/
int
s_dce_call_header(int callid, int allochint, unsigned short opnum,
		  int first, int last)
{

  unsigned char flags;
  /*DCE HEADER*/

  /*version*/
  s_binary("05");

  /*minor version*/
  s_binary("00");
  
  s_binary(REQUESTPACKET);

  /*set up the flags. DCERPC is pretty simple. FIRST and LAST packets
    are marked with flags, and anything in the middle has 0s*/
  flags=0;
  if (first)
    flags|=FIRSTFRAG;
  if (last)
    flags|=LASTFRAG;
  s_push(&flags,1);

  /*now set Data Rep, whatever that is. Check dcetest for more info, I
    think. */
  s_intelword(0x00000010);

  /*now we have 2 bytes of frag length*/
  s_binary_block_size_intel_halfword("DCEFragLength");

  /*auth length*/
  s_binary_block_size_intel_halfword("DCEAuthLength");

  /*call ID */
  s_intelword(callid);

  /*alloc hint*/
  s_intelword(allochint);

  /*context ID*/
  s_binary("0000");

  s_intelhalfword(opnum);

  /*END OF HEADER*/

  s_block_start("DCEAuthLength");
  s_block_end("DCEAuthLength");
  /*caller must end this explicitly*/
  s_block_start("DCEFragLength");

  return 1;
}

int
s_send_dce_fragment(unsigned char * buffer, unsigned int length,int first, int last,unsigned short function_number,int callid, unsigned long allochint)
{

  spike_clear();
  
  /*2nd arg is alloc hint*/
  s_dce_call_header(callid, allochint, function_number,first,last);
  /*
  printf("pushing length %d\n",length);
  */
  s_push(buffer,length);
  s_block_end("DCEFragLength");

  /*
  printf("Current TCP socket fd,size: %d %d\n",get_spike_fd(),s_get_size());
  */
  if (spike_send()<0)
    {
      printf("Couldn't send dce fragment!\r\n");
      exit(-1);
    }

  /*
  printf("After spike_send\n");
  */
/*sadly, flushes arn't working, so we will use this to flush instead*/
  sleep(0);
  /*future versions will actually parse this response and report errors*/
  return 1;
}

/*takes in a buffer and funnels all that data down to it*/
/*assumes bind has already been done*/
int
s_do_dce_call(unsigned short function_number,unsigned char * buffer, unsigned int length)
{
  struct spike * old_spike;
  struct spike * dce_spike;
  unsigned int left;
  int sendlength;
  unsigned char *p;
  int first, last;
  int done;
  static int callid=140;


  old_spike=getcurrentspike();
  dce_spike=new_spike();
  setspike(dce_spike);
  /*we assume this is already bound to DCE port*/
  dce_spike->fd=(old_spike->fd);
  dce_spike->proto=old_spike->proto;

  /*now we need to set up the calls*/
  left=length;
  first=1;
  last=0;
  sendlength=MAXDCEFRAGLENGTH;
  p=buffer;
  done=0;

  callid++;

  /*we do a max of 5000 bytes in a call, the windows stack does like
    5480 or something, but whatever*/
  /*now we do 5480, just in case that matters - hmm. freedce seems to want it all part of one tcp packet for some reason. We can do that, it doesn't seem to really matter*/
  while (!done)
    {
      /*send this fragment*/
      if (left<=MAXDCEFRAGLENGTH)
	{
	  if (!first)
	    printf("Doing last\n");
	  /*last fragment*/
	  last=1;
	  sendlength=left;
	}
      
      /*error returns 0*/
      if (!s_send_dce_fragment(p,sendlength,first,last,function_number,
			       callid,left))
	{
	  printf("Error sending dce fragment\n");
	  return 0;
	}

      /*clumsy, but should work*/
      if (left>MAXDCEFRAGLENGTH)
      {
	left-=MAXDCEFRAGLENGTH;
	p+=MAXDCEFRAGLENGTH;
	first=0;
      }
      else
      {
	/*done with all the fragments*/
	done=1;
      }
    }

  setspike(old_spike);
  spike_free(dce_spike);

  return 1;
}














