//This plugin was written by <david dot maciejak at kyxar dot fr>
//under GPLv2 license
//
//PC-Anywhere authentication protocol test on Symantec PC-Anywhere 10.5
//

#include "hydra-mod.h"

extern char *HYDRA_EXIT;

int pcadebug=0;

int send_cstring(int s, char *crypted_string)
{
  char buffer2[100],*bptr = buffer2;
  char clientcryptheader[]="\x06";
  
  memset(buffer2, 0, sizeof(clientcryptheader));
  bptr=buffer2;
  buffer2[0]=6;
  bptr++;
  buffer2[1]=strlen(crypted_string);
  bptr++;
  strcpy(bptr, crypted_string);

  return hydra_send(s,  buffer2, 2+strlen(crypted_string) , 0);
}

void show_buffer (char *buffer, int size)
{
  int i;
  
  printf("size: %d, buffer:\n",size);
  for (i=0;i<size;i++)
  {
  	printf("%c",buffer[i]);
  }
  printf("\n");
}

void clean_buffer (char *buf, int size)
{
	int i;
	for (i=0;i<size;i++)
	{
		int pos=buf[i];
		if (pos<32 || pos >126)
		{
			// . char
			buf[i]=46;
		}
	}
}

void print_encrypted_str(char* str)
{
   int i;
   printf("encode string: ");
   for (i=0;i<strlen(str);i++)
   {
	  printf("%x ", str[i]);
   }
   printf("\n");
}  

void pca_encrypt (char *cleartxt)
{
       char passwd[128];
       int i;
       
       strcpy(passwd, cleartxt);
	if (strlen(cleartxt)>0)
	{
	   passwd[0]= (passwd[0] ^ 0xab);
	   for (i=1;i<strlen(passwd);i++) 
             passwd[i] = passwd[i-1] ^ passwd[i] ^ (i-1);
	   passwd[strlen(passwd)]='\0';
	   strcpy(cleartxt, passwd);
	}

}

void pca_decrypt (char *password)
{  
	char cleartext[128];
	int i;
	
	if (strlen(password)>0)
	{
          cleartext[0]=password[0] ^ 0xab;
          for (i=1;i<strlen(password);i++) 
            cleartext[i] = password[i-1] ^ password[i] ^ (i-1);
          cleartext[strlen(password)]='\0';
	  strcpy(password,cleartext);
	}
}

void debugprintf(char *msg)
{
	if (pcadebug)
	  printf("debug: %s\n",msg);
}

int
start_pcanywhere(int s, unsigned long int ip, int port, unsigned char options, char *miscptr, FILE * fp)
{
  char *empty = "";
  char *login, *pass;
  struct in_addr remote_ip;
  char buffer[2048] = "";
  char clogin[128]="";
  char cpass[128]="";
  int ret,i;

  char *client[4];
  char *server[5];
  int clientsize[4];
  client[0]="\x00\x00\x00\x00";
  clientsize[0]=4;
  client[1]="\x6F\x06\xff"; 
  clientsize[1]=3;
  client[2]="\x6f\x61\x00\x09\x00\xfe\x00\x00\xff\xff\x00\x00\x00\x00";
  clientsize[2]=14;
  client[3]="\x6f\x62\x01\x02\x00\x00\x00";
  clientsize[3]=7;

  server[0]= "nter";
  server[1]="\x1B\x61"; 
  server[2]="\0x1B\0x62";
  server[3]="Enter login name";
  server[4]="denying connection";


  remote_ip.s_addr = ip;

      if (strlen(login = hydra_get_next_login()) == 0)
	      login = empty;
      if (strlen(pass = hydra_get_next_password()) == 0)
	      pass = empty;

  debugprintf("dans pcanywhere start");

  /*printf("testing %s:%s\n",login,pass);*/

  strcpy(clogin,login);
  strcpy(cpass,pass);

  pca_encrypt(clogin);
  pca_encrypt(cpass);

  for (i=0;i<4;i++)
  {
    if (hydra_send(s, client[i], clientsize[i], 0) < 0) {
      return 1;
    }

    ret = hydra_recv(s, buffer, sizeof(buffer));
    if (ret == -1) {
  	  return 1;
    }

    if (i==3)
    {
  	  if (ret==3)
	  {	
		  /*one more to get the login prompt*/
		  ret = hydra_recv(s, buffer, sizeof(buffer));
	  }
    }

    if (i==0 || i==3)
      clean_buffer(buffer,ret);

    /*show_buffer(buffer,ret);*/

    if (i==2)
    {
       clean_buffer(buffer,ret);
       if (strstr(buffer, server[i+2]) != NULL)
       {
	       fprintf(stderr, "PC Anywhere host denying connection because you have requested a lower encrypt level\n");
	       return 4;
       }
    }    

    if (strstr(buffer, server[i]) == NULL)
    {
	if (i==3)
	{
    	   debugprintf("problem receiving login banner");
	}
	return 1;
    }
  }  

  if (send_cstring(s,clogin) < 0) {
    return 1;
  }
  ret = hydra_recv(s, buffer, sizeof(buffer));
  if (ret == -1) {
  	return 1;
  }  
  clean_buffer(buffer,ret);
  /*show_buffer(buffer,ret);*/
  if (strstr(buffer, "Enter password:") == NULL)
  {
  	debugprintf("problem receiving password banner");
	return 1;	
  }

  if (send_cstring(s,cpass) < 0) {
    return 1;
  }
  
  ret = hydra_recv(s, buffer, sizeof(buffer));
  if (ret == -1) {
  	return 1;
  }
  
  clean_buffer(buffer,ret);
  /*show_buffer(buffer,ret);*/
  
  if ((strstr(buffer, "Invalid login") != NULL)||(strstr(buffer, "Enter password") != NULL))
  {
     debugprintf("login/passwd wrong");
     
     hydra_completed_pair();
     if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
         return 3;
     return 2;  
  }
  else 
  {
     debugprintf("cool find login/passwd");

     hydra_report_found_host(port, ip, "pcanywhere", fp);
     hydra_completed_pair_found();
     if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
	     return 3;
     return 2;
  }
  return 1;
}

void
service_pcanywhere(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port)
{
	int run = 1, next_run, sock = -1;
	int myport = PORT_PCANYWHERE, mysslport = PORT_PCANYWHERE_SSL;

	hydra_register_socket(sp);
	if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
		return;
	
	while (1)
	{

		switch (run)
		{
			case 1:                    /* connect and service init function */
				if (sock >= 0)
					sock = hydra_disconnect(sock);
				usleep(275000);
				if ((options & OPTION_SSL) == 0) 
				{
					if (port != 0)
						myport = port;
					sock = hydra_connect_tcp(ip, myport);
					port = myport;
				} else {
					if (port != 0)
						mysslport = port;
					sock = hydra_connect_ssl(ip, mysslport);
					port = mysslport;
				}
				if (sock < 0)
				{
					fprintf(stderr, "Error: Child with pid %d terminating, can not connect\n", (int) getpid());
					hydra_child_exit(1);
				}
				
				next_run = 2;
				break;

			case 2:
			                     
				next_run = start_pcanywhere(sock, ip, port, options, miscptr, fp);
				break;
			case 3:
				
				if (sock >= 0)
					sock = hydra_disconnect(sock);
				hydra_child_exit(0);
				return;
		
			default:
						
				fprintf(stderr, "Caught unknown return code, exiting!\n");
				hydra_child_exit(0);
				exit(-1);
		}
		run = next_run;
	}
}
