/* auth.c       Copyright (c) 2000-2003 Nagy Daniel
 *
 * $Date: 2003/11/11 13:55:44 $
 * $Revision: 1.2 $
 *
 * This module is the authentication layer.
 *
 *
 * 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 Library 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.
 */

#include <stdio.h>
#include <conio.h>
#include <string.h>

#include "xmalloc.h"
#include "config.h"
#include "ssh.h"
#include "sshrsa.h"
#include "sshmd5.h"
#include "transprt.h"
#include "common.h"


/* external functions, data */
extern Packet pktin;			/* incoming SSH1 packet */
extern Config GlobalConfig;		/* configuration variables */
extern unsigned short Configuration;	/* configuration bitfields */
extern char ssh1_session_id[16];	/* session ID */
extern char protocolerror[];		/* error message */


/*
 * Allocate memory for password and ask for password.
 * Don't forget to free it later.
 */
static char *AskPassword(void)
{
char *pwd;             /* password buffer */
unsigned short i = 0;  /* password length */
int c;

   pwd = xmalloc(MAX_PASSWORD_LENGTH);
   do{
      c = getch();
      if(c == 8 && i)   /* backspace */
         pwd[--i] = 0;
      else if(c >= 32)  /* normal characters */
         pwd[i++] = c;
   } while(c != 0x0d && i < MAX_PASSWORD_LENGTH);
   pwd[i] = '\0';
   putchar('\n');
   return(pwd);
}


/*
 * Send random number of ignore packets to make
 * protocol analisys harder at the authentication phase
 */
static void SendIgnores(void)
{
char rbuf[20];	/* buffer for random bytes */
unsigned short i, j, k;

   for(i = 1 + rand() % 7; i; i--){
	j = 1 + rand() % (sizeof(rbuf) - 1);
	for(k = 0; k < j; k++)
	   rbuf[k] = rand() % 256;	/* fill random buffer */
	SSH_pkt_init(SSH_MSG_IGNORE);
	SSH_putuint32(j);
	SSH_putdata(rbuf, j);
	SSH_pkt_send();
   }
}


/*
 * Send our user name to remote host
 *
 * Return 1 if we must authenticate, 0 if not
 */
short SSH1_Send_User_Name(char *name){

   if(Configuration & VERBOSE_MODE)
      puts("Sending username");
   SSH_pkt_init(SSH_CMSG_USER);
   SSH_putstring(name);
   SSH_pkt_send();

   SSH_pkt_read_sucfail();

   if(pktin.type == SSH_SMSG_FAILURE)
      return(1); /* we must authenticate */

   return(0);	/* else no authentication is needed */
}


/*
 * Try public key authentication
 *
 * Return 1 if try password auth, 0 if key is OK
 */
short SSH1_Auth_Pubkey(char *keyfile)
{
int type, i;
char *pwd = NULL, *comment = NULL;
RSAKey pubkey;
Bignum challenge, response;
char buf[32];

   memset(&pubkey, 0, sizeof(RSAKey));

   if(keyfile == NULL) /* is there a key file? */
      return(1);       /* go and try password authentication */

   if(Configuration & VERBOSE_MODE)
	puts("Begin public key authentication");

   /* Check key */
   type = key_type(keyfile);
   if(type != SSH_KEYTYPE_SSH1){
      if(Configuration & VERBOSE_MODE)
	 printf("Key is of wrong type (%s)\n", key_type_to_str(type));
	 return(1);       /* go and try password authentication */
   }

   /* Check if encrypted */
   if(rsakey_encrypted(keyfile, &comment)){
      printf("Passphrase for key \"%.100s\": ", comment);
      xfree(comment);
      pwd = AskPassword();
   } /* if */
   else	if(Configuration & VERBOSE_MODE)
      puts("No passphrase required");

   /* Try to load key */
   i = loadrsakey(keyfile, &pubkey, pwd);

   if(pwd){
      memset(pwd, 0, strlen(pwd));
      xfree(pwd);
   }

   if(!i){
	printf("Couldn't load private key from %s\n", keyfile);
	return(1);       /* go and try password authentication */
   }
   if(i == -1){
	puts("Wrong passphrase");
	return(1);       /* go and try password authentication */
   }

   /* Send a public key attempt */
   i = ssh1_bignum_length(pubkey.modulus);
   SSH_pkt_init(SSH_CMSG_AUTH_RSA);
   SSH_putbn(pubkey.modulus);
   SSH_pkt_send();

   SSH_pkt_read(0);
   if(pktin.type == SSH_SMSG_FAILURE){
	puts("Host refused the key");
	return(1);       /* go and try password authentication */
   }
   if(pktin.type != SSH_SMSG_AUTH_RSA_CHALLENGE)
	SSH_Disconnect(protocolerror);

   if(Configuration & VERBOSE_MODE)
	puts("Got RSA challenge, calculating response");
   ssh1_read_bignum(pktin.body, &challenge);

   response = rsadecrypt(challenge, &pubkey);
   freebn(challenge);
   freersakey(&pubkey);	/* burn the evidence */

   for (i = 0; i < 32; i++)
	buf[i] = bignum_byte(response, 31 - i);

   MD5Init();
   MD5Update(buf, 32);
   MD5Update(ssh1_session_id, 16);
   MD5Final(buf);

   if(Configuration & VERBOSE_MODE)
	puts("Sending RSA response");

   SSH_pkt_init(SSH_CMSG_AUTH_RSA_RESPONSE);
   SSH_putdata(buf, 16);
   SSH_pkt_send();

   SSH_pkt_read_sucfail();
   if(pktin.type == SSH_SMSG_FAILURE){
      puts("Failed to authenticate with key");
      return(1);	       /* go and try password */
   }
   return(0);
}


/*
 * Try password authentication, max three times
 */
void SSH1_Auth_Password(char *pwd)
{
unsigned short n = 1;	/* number of retries */

   if(Configuration & VERBOSE_MODE)
        puts("Trying password authorization");
   if(!pwd){
      printf("Password: ");
      fflush(stdout);
      if(GlobalConfig.brailab)
         fputs("Password: ", GlobalConfig.brailab);
      pwd = AskPassword();
      n++;
   }

nextpass:
   SendIgnores();
   SSH_pkt_init(SSH_CMSG_AUTH_PASSWORD);
   SSH_putstring(pwd);
   SSH_pkt_send();
   memset(pwd, 0, strlen(pwd));
   xfree(pwd);
   SendIgnores();

   SSH_pkt_read_sucfail();

   if(pktin.type == SSH_SMSG_FAILURE){
	if(n-- == 0)
	   fatal("Invalid password");
	printf("Password: ");
	if(GlobalConfig.brailab)
	   fputs("Password: ", GlobalConfig.brailab);
	pwd = AskPassword();
	goto nextpass;
   } /* if */
}
