/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 *
 * Copyright 1989 - 1994, Julianne Frances Haugh
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <strings.h>
#include <locale.h>
#include <stdio.h>

#include <form.h>
#include <netdb.h>
#include <unistd.h>

#include "auth.h"
#include "firewall.h"
#include "firewall2.h"

#include "prototypes.h"

#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>


#include <signal.h>

#include <curses.h>


#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#define MAX_STR_LEN 500
#define AUTH_SUCCESS   1
#define AUTH_READ_MORE 2
#define REQUEST_UNAME  3
#define REQ_PWD        4
#define AUTH_DENY      5
#define AUTH_ERR       6

#define TXT_NAME 0
#define TXT_PWD  1
#define CNTL_NUM 2



#define STRFCPY(A,B) \
	(strncpy((A), (B), sizeof(A) - 1), (A)[sizeof(A) - 1] = '\0')


int curses_initialized = 0;

void lostconn();
int login_dlg(char who[], int buf_len);
int send_pwd(char pwd[]);
int send_uname(char uname[]);
int process_authresp();
int auth_console(char host_addr[], char user_name[]);
int auth_login(char hostaddr[]);


char *hostaddr = "";

struct	passwd	pwent;
#if HAVE_UTMPX_H
struct	utmpx	utxent, failent;
struct	utmp	utent;
#else
struct	utmp	utent, failent;
#endif

/*
 * Global variables.
 */

static int amroot;
static int timeout_alarm;

#define WLCM_MSG_ROW   1
#define WLCM_MSG_COL   1
char   welcome_msg[MAX_STR_LEN] = "";

/*
 * External identifiers.
 */

extern char **newenvp;
extern size_t newenvc;

extern	int	optind;
extern	char	*optarg;
extern	char	**environ;

extern int login_access(const char *, const char *);
extern void login_fbtab(const char *, uid_t, gid_t);

int auth_user(char *authsrv_host, unsigned short int authsrv_port, char username[]);
int process_resp(char *resp, char *req);
int extract_username(char *raw, char username[]);
void ShowUsage();


#ifndef	ALARM
#define	ALARM	1000
#endif

#ifndef	RETRIES
#define	RETRIES	3
#endif


#define	NO_SHADOW	"no shadow password for `%s'%s\n"
#define	BAD_PASSWD	"invalid password for `%s'%s\n"
#define	BAD_DIALUP	"invalid dialup password for `%s' on `%s'\n"
#define	BAD_TIME	"invalid login time for `%s'%s\n"
#define	BAD_ROOT_LOGIN	"ILLEGAL ROOT LOGIN%s\n"
#define	ROOT_LOGIN	"ROOT LOGIN%s\n"
#define	FAILURE_CNT	"exceeded failure limit for `%s'%s\n"
#define REG_LOGIN	"`%s' logged in%s\n"
#define LOGIN_REFUSED	"LOGIN `%s' REFUSED%s\n"
#define REENABLED2 \
	"login `%s' re-enabled after temporary lockout (%d failures).\n"
#define MANY_FAILS	"REPEATED login failures%s\n"

/* local function prototypes */


static void init_env(void);
static void alarm_handler(int);


static void
init_env(void)
{
       char* tmp;

       if ((tmp = getenv("COLUMNS"))) {
               addenv("COLUMNS", tmp);
       }

       if ((tmp = getenv("LINES"))) {
               addenv("LINES", tmp);
       }

       if ((tmp = getenv("TERM"))) {
               addenv("TERM", tmp);
       }


	if ((tmp = getenv("LANG"))) {
		addenv("LANG", tmp);
	}

	/*
	 * Add the timezone environmental variable so that time functions
	 * work correctly.
	 */

	if ((tmp = getenv("TZ"))) {
		addenv("TZ", tmp);
	}

}


static void
alarm_handler(int sig)
{
}



int auth_login(char hostaddr[])
{
	char	username[MAX_STR_LEN + 1];
	char	tty[BUFSIZ];


	int	delay;
	int	retries;
	char	*tmp;

	char buf[550];

	int y, x;



	/*
	 * Some quick initialization.
	 */

	initenv();

	init_env();

	environ = newenvp;		/* make new environment active */

	username[0] = '\0';
	amroot = (getuid() == 0);



/*  MYCHANGE: Look at it later
	if (!isatty(0) || !isatty(1) || !isatty(2))
		exit(1);   */		/* must be a terminal */


	STRFCPY(tty, utent.ut_line);

	(void) alarm (0);		/* turn off alarm clock */



	signal(SIGQUIT, SIG_DFL);	/* default quit signal */
	signal(SIGTERM, SIG_DFL);	/* default terminate signal */
	signal(SIGALRM, SIG_DFL);	/* default alarm signal */
	signal(SIGHUP, SIG_DFL);	/* added this.  --marekm */


	signal(SIGINT, SIG_DFL);	/* default interrupt signal */

	umask(077);

	sosetline(SO_LLF);

	/* this uses confp to get server/port numbers */
	if(auth_open(proxy_confp)) {
		fprintf(stderr,"Cannot open auth service\n");
		exit(1);
	}

	if(auth_recv(buf,sizeof(buf)))
		lostconn();
	if(strncmp(buf,"Authsrv ready",13)) {
		fprintf(stderr,"Cannot connect to server, response: %s\n",buf);
		exit(1);
	}
	fprintf(stderr, "Connected to server\n");
	
	initscr();                        /* */
        kill(0, SIGWINCH);                /* Hack to get a real window size when  */
	cbreak(); halfdelay(1); getch();  /* connecting from xterm:  */                                 
        endwin();                         /* just pretend that the window has been  */
	                                  /* resized (SIGWINCH), getch - for KEY_RESIZE (as far as I understand). */
					  /* But there could be no chars to read, so getch is in nonblocking mode */
					  /* (max wait for 0.1 sec) */
                    			  /* Then re-init ncurses. */
					  /* */

        initscr(); curses_initialized = 1;

        move(10, 15);             /* Check whether the terminal can move a cursor */
	refresh();
	getyx(stdscr, y, x);
	if(y != 10 || x != 15)  {
	  endwin();
	  fprintf(stderr, "Your terminal is unable to change a cursor position\n");
          syslog(LOG_ERR, "Your terminal is unable to change a cursor position\n");
	  exit(1);
	}

	scrollok(stdscr, 1);

	start_color();
   	init_pair(1, COLOR_GREEN, COLOR_BLACK);
        init_pair(2, COLOR_BLACK, COLOR_WHITE);


	/* only allow ALARM sec. for login */
	signal(SIGALRM, alarm_handler);
	timeout_alarm = ALARM;
	if (timeout_alarm > 0)
		alarm(timeout_alarm);

	delay = 1;
	retries = RETRIES;

	if((tmp = proxy_conf_string(proxy_confp, "welcome-msg"))) {
	  strncpy(welcome_msg, tmp, sizeof(welcome_msg) - 1);
	  welcome_msg[sizeof(welcome_msg) - 1] = '\0';
	}


        if(login_dlg(username, MAX_STR_LEN) == -1)  {
          endwin();
          exit(1);
        }

	/* Could be here if authenticated successfully only */

	auth_close();

	auth_console(hostaddr, username);

	return 0;
}


int extract_username(char *raw, char username[])
{
  char *startp, *endp;

  if((startp = strstr(raw, " ")))
  {
    (void)((endp = strstr(++startp, " ")) || (endp = strstr(startp, "\n")) || (endp = &raw[strlen(raw)]));
    if(MAX_STR_LEN < (endp - startp)) return 0;
    strncpy(username, startp, endp - startp);
    username[endp - startp] = '\0';
    return 1;
  }
  return 0;
}
  
    
int login_dlg(char who[], int buf_len)
{
	WINDOW *login_win, *login_frame_win, *note_win;
	int ch, rows, cols;

	int allow_auth = 1;
	FIELD *field[3];
	FORM  *login_form;
	int curr_cntl;
	int resp = 0;
	FILE *fp;
        int c;
	
	if(strlen(welcome_msg) > 0 && (fp = fopen(welcome_msg, "r"))) {
	  wmove(stdscr, WLCM_MSG_ROW, WLCM_MSG_COL);
	  while((c = fgetc(fp)) != EOF) {
	    addch(c);
	  }
	  fclose(fp);
	  wrefresh(stdscr);
	}

	noecho();
	
        login_frame_win = newwin(13, 64, 5, 8);
	wbkgd(login_frame_win, COLOR_PAIR(1));
	login_win = derwin(login_frame_win,7, 62, 1, 1);
	
        note_win = derwin(login_frame_win,2, 56, 10, 3);				    	

        keypad(login_win, TRUE);
	
	field[0] = new_field(1, 46, 2, 13, 0, 1);            /* Username */
	field[1] = new_field(1, 46, 5, 13, 0, 1);            /* Password */
	field[2] = NULL;

	set_field_back(field[0], A_UNDERLINE | COLOR_PAIR(1)); 	
	set_field_fore(field[0], COLOR_PAIR(1));	
	field_opts_off(field[0], O_AUTOSKIP);  	
      						
							
	set_field_back(field[1], A_UNDERLINE | COLOR_PAIR(1)); 
	set_field_fore(field[1], COLOR_PAIR(1));
	field_opts_off(field[1], O_AUTOSKIP);

	login_form = new_form(field);
	form_opts_off(login_form, O_BS_OVERLOAD);		
	
	scale_form(login_form, &rows, &cols);

	set_form_win(login_form, login_frame_win);
	set_form_sub(login_form, login_win);
	box(login_frame_win,0, 0);
	mvwprintw(login_frame_win, 0, 2, " Login auth console ");

	post_form(login_form);
	wrefresh(login_frame_win);


	mvwprintw(login_win, 2, 3, "Username:");
	mvwprintw(login_win, 5, 3, "Response:");
	wmove    (login_win, 2,13);
	wrefresh (login_win);

	curr_cntl = TXT_NAME;

	pos_form_cursor(login_form);

        do
	{

	        if((ch = wgetch(login_win)) == -1 && 0)  {
                  allow_auth = -1;
                  goto cleanout;
                }

		switch(ch)
		{	case KEY_DOWN:
			case KEY_UP  :
		        case '\t'    :
		        case '\n'    :
		        case '\r'    :
				form_driver(login_form, REQ_NEXT_FIELD);

                                if(curr_cntl == TXT_NAME)  {
/*				  if(field_status(field[0])) {
*/
				    werase(note_win);
				    wrefresh(note_win);

				    send_uname(field_buffer(field[0],0));
/*				  }
*/
				}
				else if(curr_cntl == TXT_PWD) {
				  send_pwd(field_buffer(field[1], 0));
				}
		                
				curs_set(0); mvwaddstr(login_win,6, 3, "(Just a minute, please...)\n"); wrefresh(login_win);				
				
				if((resp = process_authresp(login_form, note_win))== AUTH_SUCCESS) {
				    break;                     /* SUCCESS. fall out from the "switch" and then from the "while" */
				}
				else if(resp == REQ_PWD)  {
				    curr_cntl = TXT_PWD - 1;   /* don't change currect control */
				}
				else if(resp == AUTH_DENY)  {  /* Failed to login. Starting over again from username */
				}

				++curr_cntl;
				curr_cntl %= CNTL_NUM;
				
				set_field_buffer(field[1], 0, "");   /* Empty password field whenever switch currect field */

				curs_set(1);

				form_driver(login_form, REQ_END_LINE);				
				wrefresh(login_win);
				break;
			case KEY_BACKSPACE:
				form_driver(login_form, REQ_DEL_PREV);
				break;
			case KEY_DC:
				form_driver(login_form, REQ_DEL_CHAR);
				break;
			case KEY_LEFT:
				form_driver(login_form, REQ_LEFT_CHAR);
				break;
			case KEY_RIGHT:
				form_driver(login_form, REQ_RIGHT_CHAR);
				break;
			default:
				form_driver(login_form, ch);
				break;
		}
	} while(resp != AUTH_SUCCESS);

	strncpy(who, field_buffer(field[0],0), buf_len);
        strtrm(who);

cleanout:
	werase(note_win);
	wrefresh(note_win);
	delwin(note_win);

	unpost_form(login_form);
	free_form(login_form);
	free_field(field[0]);
	free_field(field[1]);

	delwin(login_win);
	delwin(login_frame_win);
	curs_set(1);
	refresh();

	return allow_auth;
}


int process_authresp(FORM *login_form, WINDOW *note_win)
{
        int ret_val = REQ_PWD;
	char		rbuf[1024];

	while (1) {
		if(auth_recv(rbuf,sizeof(rbuf)))
			lostconn();

/*
		if(wgetch(note_win) == 'd')
		  strlcpy(rbuf, "display your password will expire in 15 days (bla bla bla)\n", sizeof(rbuf));
*/		  
		
		
		strtrm(rbuf);
		
		if(!strncmp(rbuf,"ok",2)) {
		        mvwprintw(form_sub(login_form),6, 3, "(OK)\n");		
		        ret_val = AUTH_SUCCESS;
			break;
		}
		if(!strncmp(rbuf,"display ",8)) {
			wprintw(note_win,"%s",&rbuf[8]);
			wrefresh(note_win);			
			strlcpy(rbuf,"response dummy",sizeof(rbuf));
			if(auth_send(rbuf))
				lostconn();
			continue;
		}
		if(!strncmp(rbuf,"challenge ",10)) {
		        if(rindex(rbuf, ':')) *(rindex(rbuf, ':')) = '\0';		
		        mvwprintw(form_sub(login_form),6, 3, "(%s)\n", &rbuf[10]);

			form_driver(login_form, REQ_NEXT_FIELD);	     /* It's impossible to change       */
			field_opts_on(form_fields(login_form)[1], O_PUBLIC); /* an option of the current field. */
			form_driver(login_form, REQ_PREV_FIELD);	     /* Don't know why.
									      * Workaround - temporarily change
									      * current field and then restore it.
									      */
			break;
		} else
		if(!strncmp(rbuf,"chalnecho ",10)) {
		        if(rindex(rbuf, ':')) *(rindex(rbuf, ':')) = '\0';
		        mvwprintw(form_sub(login_form),6, 3, "(%s)\n", &rbuf[10]);

			form_driver(login_form, REQ_NEXT_FIELD);	      /* It's impossible to change       */
			field_opts_off(form_fields(login_form)[1], O_PUBLIC); /* an option of the current field. */
			form_driver(login_form, REQ_PREV_FIELD);	      /* Don't know why.
									       * Workaround - temporarily change
									       * current field and then restore it.
									       */
			break;
		} else
		if(!strncmp(rbuf,"password",8)) {
		        mvwaddstr(form_sub(login_form),6, 3, "(Password)\n");

			form_driver(login_form, REQ_NEXT_FIELD);	      /* It's impossible to change       */
			field_opts_off(form_fields(login_form)[1], O_PUBLIC); /* an option of the current field. */
			form_driver(login_form, REQ_PREV_FIELD);	      /* Don't know why.
									       * Workaround - temporarily change
									       * current field and then restore it.
									       */
			post_form(login_form);			
			break;
		} else 
		if(!strncmp(rbuf,"Permission Denied.",18)) {
		        mvwaddstr(form_sub(login_form),6, 3, "(Permission Denied)\n");
			return AUTH_ERR;
		} else {
		        mvwprintw(form_sub(login_form),6, 3, "(Unrecognized authsrv response: %s)\n", rbuf);
			return AUTH_ERR;
		}
		return AUTH_DENY;
	}
	wrefresh(form_sub(login_form));
	
	return ret_val;
}

int send_uname(char uname[])
{
	char		rbuf[1024];
	char		myhostname[512];
	struct	hostent	*hp;
	
	char buf[1024];
	strlcpy(buf, uname, sizeof(buf) );
        strtrm(buf);
	
	if(gethostname(myhostname,sizeof(myhostname)))
		strlcpy(myhostname,"amnesiac",sizeof(myhostname));
	else if ((hp = gethostbyname(myhostname)) != (struct hostent *)0)
		strlcpy(myhostname,hp->h_name,sizeof(myhostname));
	
	snprintf(rbuf,sizeof(rbuf),"authorize %.32s 'authcons %.128s/%.128s'",buf,proxy_stats.rladdr,proxy_stats.riaddr);
	if(auth_send(rbuf))
		lostconn();
        return 1;
}

int send_pwd(char pwd[])
{
	char rbuf[1024];
	char buf[1024];
	strlcpy(buf, pwd, sizeof(buf) - 13);
        strtrm(buf);
	snprintf(rbuf, sizeof(rbuf), "response '%s'", buf);
	if(auth_send(rbuf))
		lostconn();
	return 1;
}


void lostconn()
{
        if(curses_initialized)  endwin();
	fprintf(stderr,"Lost connection to auth service\n");
	auth_close();
	exit(1);
}
