#include "config_xor.h"

#ifdef HAVE_BROKEN_INCLUDES
#define _ANSI_C_SOURCE
#define _POSIX_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>


#ifdef SH_STEALTH
char * globber(const char * string);
#define _(string) globber(string) 
#define N_(string) string
#else
#define _(string)  string 
#define N_(string) string
#endif

#ifdef SH_STEALTH
#ifndef SH_MAX_GLOBS
#define SH_MAX_GLOBS 32
#endif
char * globber(const char * str)
{
  register int i, j;
  static int  count = -1;
  static char glob[SH_MAX_GLOBS][128];

  ++count; if (count > (SH_MAX_GLOBS-1) ) count = 0;
  j = strlen(str);
  if (j > 127) j = 127;

  for (i = 0; i < j; ++i)
    {
      if (str[i] != '\n' && str[i] != '\t') 
	glob[count][i] = str[i] ^ XOR_CODE;
      else
	glob[count][i] = str[i];
    }
  glob[count][j] = '\0';
  return glob[count];
}
#endif

/* This is a very inefficient algorithm, but there is really no
 * need for anything more elaborated here. Can handle NULL's in haystack
 * (not in needle), which strstr() apparently cannot.
 */
char * my_strstr (char * haystack, char * needle, int haystack_size)
{
  register int      i = 0, j = 0;
  register int      siz;
  register char * ptr = haystack;
  register int      len;

  siz = strlen(needle);
  len = haystack_size - siz;

  while (j < len)
    {
      i = 0;
      while (i < siz)
	{
	  if (needle[i] != ptr[i]) break;
	  if (i == (siz-1)) 
	      return ptr;
	  ++i;
	}
      ++ptr; ++j;
    }
  return NULL;
}

/* fread()  does not return the number of chars read, thus we need to
 * read only a small number of bytes, in order not to expand the binary
 * too much with the last fwrite(). Too lazy to fix this now. 
 */
#define GRAB_SIZE 1024

int readhexchar ( char c )
{
  if      ( c >= '0' && c <= '9' )
    return c - '0';
  else if ( c >= 'a' && c <= 'f' )
    return c - 'a' + 10;
  else if ( c >= 'A' && c <= 'F' )
    return c - 'A' + 10;
  else return -1;
}

int main (int argc, char * argv[])
{
  /* the default password
   */
  unsigned char TcpFlag[9] = { 0xF7,0xC3,0x12,0xAA,0xAA,0x12,0xC3,0xF7 }; 
  unsigned char BadFlag[9] = { 0xFF,0xC3,0x12,0xAA,0xAA,0x12,0xC3,0xFF }; 
  
  char * found_it;
  int    i;
  int    suc    = 0;
  int    badcnt = 0;

  char * newn;
  int    oldf;
  int    newf;

  unsigned long bytecount;

  char   in[9];
  int    j, k;
  char   ccd;
  char * str;

  char * buf = (char *) malloc(GRAB_SIZE);
  size_t dat;
  char * newpwd = (char *) malloc(5 * 8 + 2); 
  char * oldpwd = (char *) malloc(5 * 8 + 2); 

  memset (newpwd, '\0', 5 * 8 + 2); 
  memset (oldpwd, '\0', 5 * 8 + 2); 


  if (argc < 4) 
    {
      fprintf (stderr, _("\nUsage: samhain_setpwd <filename> <suffix> "\
	       "<new_password>\n\n"));
      fprintf (stderr, _("   This program is a utility that will:\n"));
      fprintf (stderr, _("    - search in the binary executable <filename> "\
	       "for samhain's\n"));
      fprintf (stderr, _("      compiled-in default password,\n"));
      fprintf (stderr, _("    - change it to <new_password>,\n"));
      fprintf (stderr, _("    - and output the modified binary to "\
	       "<filename>.<suffix>\n\n"));
      fprintf (stderr, _("   To allow for non-printable chars, "\
			 "<new_password> must be\n")); 
      fprintf (stderr, _("   a 16-digit hexadecimal "\
	       "number (only 0-9,A-F allowed in input),\n"));
      fprintf (stderr, _("   thus corresponding"\
			 "   to an 8-byte password.\n\n"));
      fprintf (stderr, _("   Example: 'samhain_setpwd samhain new "\
	       "4142434445464748'\n"));
      fprintf (stderr, _("   takes the file 'samhain', sets the password to "\
	       "'ABCDEFGH'\n")); 
      fprintf (stderr, _("   ('A' = 41 hex, 'B' = 42 hex, ...) "\
	       "and outputs the result\n"));
      fprintf (stderr, _("   to 'samhain.new'.\n"));
      return  EXIT_FAILURE;
    }

  if (strlen(argv[3]) != 16)
    {
      fprintf (stdout, _("ERROR <new_password> %s has not exactly 16 chars\n"),
	       argv[0]);
      fflush(stdout);
      return  EXIT_FAILURE;
    }


  str = &argv[3][0];
  i = 0;
  while (i < 16)
    {
      k = i/2; j = 0; 
      if (2*k == i) in[k] = 0;
      while (j < 16)
	{
	  if (-1 != readhexchar(str[i])) 
	    {
	      in[k] += readhexchar(str[i]) * (i == 2*k ? 16 : 1);
	      break;
	    }
	  ++j;
	  if (j == 16) 
	    {
	      fprintf(stdout, _("ERROR Invalid char %c\n"), str[i]);
	      fflush(stdout);
	      return EXIT_FAILURE;
	    }
	}
      ++i;
    }
  in[8] = '\0';

  /* ---- initialize -----
   */
  (void) umask (0);

  srand(time(NULL) ^ getpid());

  bytecount = 0;


  /* ---- open files -----
   */
  
  oldf = open(argv[1], O_RDONLY);

  newn = (char *) malloc (strlen(argv[1])+strlen(argv[2])+2);
  strcpy(newn, argv[1]);
  strcat(newn, ".");
  strcat(newn, argv[2]);
  newf = open(newn, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);

  if (oldf < 0)
    {
      fprintf(stdout, _("ERROR Cannot open input file %s.\n"), argv[1]);
      fflush(stdout);
      return EXIT_FAILURE;
    }
  if (newf < 0)
    {
      fprintf(stdout, _("ERROR Cannot open output file %s.\n"), newn);
      fflush(stdout);
      return EXIT_FAILURE;
    }
      
  /* ---- scan file -----
   */
  

  while (1)
    {
      dat = read (oldf, buf, GRAB_SIZE); 
      if (dat == 0) 
	break;

      bytecount += dat;

      while ( (found_it = my_strstr(buf, (char *) TcpFlag, GRAB_SIZE)) != NULL)
	{
	  suc = 1;
	  fprintf (stdout, _("INFO   old password found\n"));
	  fflush(stdout);
	  for (i = 0; i < 8; ++i)
	    {
	      sprintf(&oldpwd[i*2], _("%02x"), 
		      (unsigned char) *found_it);
	      sprintf(&newpwd[i*2], _("%02x"), 
		      (unsigned char) in[i]);
	      *found_it = in[i];
	      ++found_it;
	    }
	  fprintf (stdout, _("INFO   replaced:  %s  by:  %s\n"), 
		   oldpwd, newpwd);
	  fflush(stdout);
	}

      while ( (found_it = my_strstr(buf, (char *) BadFlag, GRAB_SIZE)) != NULL)
	{
	  badcnt++;
	  /* fprintf (stderr, _("INFO   old filler found\n")); */
	  for (i = 0; i < 8; ++i)
	    {
	      sprintf(&oldpwd[i*2], _("%02x"), 
		      (unsigned char) *found_it);

	      ccd = (unsigned char) (256.0 * rand()/(RAND_MAX+1.0));
	      sprintf(&newpwd[i*2], _("%02x"), 
		      (unsigned char) ccd);
	      *found_it = ccd;

	      ++found_it;
	    }
	  /* fprintf (stderr, _("INFO   replaced:  %s  by:  %s\n"), 
	     oldpwd, newpwd);
	  */
	}


      write (newf, buf, dat);
    }

  if (suc == 1 && badcnt == 7)
    {
      fprintf (stdout, _("INFO   finished\n"));
      close (newf);
      close (oldf);
      fflush(stdout);
      return (0);
    }

  lseek (oldf, 0, SEEK_SET);
  lseek (newf, 0, SEEK_SET);

  fprintf (stdout, _("INFO   Not found in first pass.\n"));
  fprintf (stdout, _("INFO   Second pass ..\n"));

  /* offset the start point
   */

  dat = read (oldf, buf, (GRAB_SIZE / 2));
  write (newf, buf, dat);

  bytecount = 0;
  suc       = 0;
  badcnt    = 0;

  while (1)
    {
      dat = read (oldf, buf, GRAB_SIZE); 
      if (dat == 0) 
	break;

      bytecount += dat;

      while ( (found_it = my_strstr(buf, (char *) TcpFlag, GRAB_SIZE)) != NULL)
	{
	  suc = 1;
	  fprintf (stdout, _("INFO   old password found\n"));
	  for (i = 0; i < 8; ++i)
	    {
	      sprintf(&oldpwd[i*2], _("%02x"), 
		      (unsigned char) *found_it);
	      sprintf(&newpwd[i*2], _("%02x"), 
		      (unsigned char) in[i]);
	      *found_it = in[i];
	      ++found_it;
	    }
	  fprintf (stdout, _("INFO   Replaced:  %s  by:  %s\n"), 
		   oldpwd, newpwd);
	}

      while ( (found_it = my_strstr(buf, (char *) BadFlag, GRAB_SIZE)) != NULL)
	{
	  badcnt++;
	  /* fprintf (stderr, _("INFO   old filler found\n")); */
	  for (i = 0; i < 8; ++i)
	    {
	      sprintf(&oldpwd[i*2], _("%02x"), 
		      (unsigned char) *found_it);

	      ccd = (unsigned char) (256.0 * rand()/(RAND_MAX+1.0));
	      sprintf(&newpwd[i*2], _("%02x"), 
		      (unsigned char) ccd);
	      *found_it = ccd;

	      ++found_it;
	    }
	  /* fprintf (stderr, _("INFO   Replaced:  %s  by:  %s\n"), 
	     oldpwd, newpwd);*/
	}

      write (newf, buf, dat);
    }

  close (newf);
  close (oldf);

  if (suc == 1 && badcnt == 7)
    {
      fprintf (stdout, _("INFO   finished\n"));
      fflush(stdout);
      return 0;
    }

  if (suc == 0 || badcnt < 7)
    {
      fprintf (stdout, _("ERROR incomplete replacement\n"));
    }
  else 
    {
      fprintf (stdout, _("ERROR bad replacement\n"));
    }
  fflush(stdout);
  return EXIT_FAILURE;
}
