/*
 * OpenFWTK authentication database manager client
 * Copyright (c) 2003-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 * (C) 2003 ADVA Research Center
 * (C) 2003 Alexei Kravchuk 
 * (C) 2003 ArkanoiD, portability modifications
 */

#include	<stdio.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<syslog.h>
#include	<netdb.h>

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

#include	<string.h>
#include	<strings.h>

static	char	*moduleId ATTR_UNUSED = "$Id: authmgr.c,v 1.7 2007/09/10 02:49:12 arkenoi Exp $";

extern	char	*getpassword();
void	lostconn();

#define	FLG_LOCAL	1
typedef	struct {
	char	*cnam;
	int	flg;
	char	*help;
	int	(*cfun)();
} Cmd;

static	int	logged_in = 0;


static	int	do_quit();
static	int	do_login();
static	int	do_passwd();
static	int	do_help();
static	int	do_multiline();
static	Cmd	*find_command();

static Cmd ctab[] = {
{"login",	FLG_LOCAL,	"login",			do_login      },
{"adduser",	0,	"adduser username [longname]",		0	      },
{"deluser",	0,		"deluser username",		0	      },
{"display",	FLG_LOCAL,	"display username",		do_multiline  },
{"list",	FLG_LOCAL,	"list [group]",			do_multiline  },
{"enable",	0,		"enable username",		0	      },
{"disable",	0,		"disable username",		0             },
{"addgroup",	0,		"addgroup username group",	0             },
{"revoke",	0,		"revoke username group",	0             },
{"rename",	0,	"rename user newname [longname]",	0             },
{"proto",	0,		"proto username auth-proto",	0             },
{"wiz",		0,		"wiz username",			0             },
{"unwiz",	0,		"unwiz username",		0	      },
{"password",	FLG_LOCAL,	"password [user [pass]]",	do_passwd     },
{"quit",	FLG_LOCAL,	"quit",				do_quit       },
{"exit",	FLG_LOCAL,	"exit",				do_quit       },
{"?",		FLG_LOCAL,	"?",				do_help       },
{"help",	FLG_LOCAL,	"help",				do_help       },
{0,		0,		0,				0	      }
};


static	int	stin;	/* terminal is standard input */
static	Cfg	*confp;


int main(ac,av)
int	ac;
char	*av[];
{
	Cmd	*cp;
	char	buf[MAX_STR];
	char	*xa[MAX_ARG];
	char	xb[MAX_STR];
	int	xc;
	char	*p;

#ifndef	LOG_NDELAY
	openlog("authmgr",LOG_PID);
#else
	openlog("authmgr",LOG_PID|LOG_NDELAY,LOG_USER);
#endif

	confp = cfg_read("authmgr");
	sotimeout(proxy_timeout);

	/* this uses confp to get server/port numbers */
	if(auth_open(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");

	stin = isatty(fileno(stdin));
	while(1) {
		if(stin) {
			fprintf(stderr,"authmgr-> ");
			fflush(stderr);
		}

		if(fgets(buf,sizeof(buf),stdin) == (char *)0)
			break;
		if((p = rindex(buf,'\n')) != (char *)0)
			*p = '\0';

		xc = enargv(buf,xa,sizeof(xa)/sizeof(char *),xb,sizeof(xb));
		if(xc < 0) {
			fprintf(stderr,"Too many command parameters.\n");
			continue;
		}
		if(xc == 0)
			continue;

		cp = find_command(ctab, xa[0]);

		if(!cp) {
			printf("Command \"%s\" is ambiguous.\n",xa[0]);
			continue;
		}

		if(cp->cnam == (char *)0) {
			printf("Command \"%s\" unrecognized.\n",xa[0]);
			continue;
		}

		if(cp->flg & FLG_LOCAL) {
			(*cp->cfun)(xc,xa,buf);
			continue;
		}

		if(auth_send(buf))
			lostconn();
		if(auth_recv(xb,sizeof(xb)))
			lostconn();
		printf("%s\n",xb);
	}
	auth_close();
	exit(0);
}

static Cmd *
find_command (tab, cmd)
Cmd * tab;
char *cmd;
{
	Cmd *cp, *match;
	int nl;

	nl = strlen(cmd);
	match = 0;
	for (cp = tab; cp->cnam != (char *)0; cp++) {
		if (!strcmp(cp->cnam, cmd))
			return cp;
		else if (!strncasecmp(cp->cnam, cmd, nl)) {
			if (match && cp->cfun != match->cfun)
				return 0; /* ambiguous */
			match = cp;
		}
	}
	return match ? match : cp;
}

static	int
do_quit()
{
	auth_close();
	exit(0);
}


static	int
do_help()
{
	Cmd	*cp;

	printf("Command List:\n");
	printf("(Commands may be invoked with unique leading abbreviation)\n");
	for(cp = ctab; cp->cnam != (char *)0; cp++)
		printf(" %s\n",cp->help);
	return(0);
}


static	int
do_login(ac,av,original)
int	ac;
char	*av[];
char	*original;
{
	char		usrbuf[MAX_STR];
	char		rbuf[MAX_STR];
	char		pbuf[MAX_STR];
	char		myhostname[MAX_HOSTNAME];
	struct	hostent	*hp;
	char		*p;

	logged_in = 0;
	if(ac > 1) 
		strlcpy(usrbuf,av[1], sizeof(usrbuf));
	else {
		fprintf(stderr,"Username: ");
		fflush(stderr);
		if(fgets(usrbuf,sizeof(usrbuf),stdin) == (char *)0)
			do_quit();
	}
	
	strtrm(usrbuf);
	
	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 'authmgr %.128s/127.0.0.1'",usrbuf,myhostname);
	if(auth_send(rbuf))
		lostconn();
	while (1) {
		if(auth_recv(rbuf,sizeof(rbuf)))
			lostconn();
		if(!strncmp(rbuf,"ok",2)) {
			break;
		}
		if(!strncmp(rbuf,"display ",8)) {
			fprintf(stderr,"%s",&rbuf[8]);
			strlcpy(rbuf,"response dummy",sizeof(rbuf));
			if(auth_send(rbuf))
				lostconn();
			continue;
		}
		if(!strncmp(rbuf,"challenge ",10)) {
			char *q;
			fprintf(stderr,"%s",&rbuf[10]);
			fflush(stderr);
			if(fgets(pbuf,sizeof(pbuf),stdin) == (char *)0)
				pbuf[0] = '\0';
			p = pbuf;
			if((q = rindex(p,'\n')) != (char *)0)
				*q = '\0';
		} else
		if(!strncmp(rbuf,"chalnecho ",10)) {
			p = getpassword(&rbuf[10]);
		} else
		if(!strncmp(rbuf,"password",8)) {
			p = getpassword("Password: ");
		} else {
			fprintf(stderr,"%s",rbuf);
			return(1);
		}
		if(p == (char *)0)
			return(1);
		snprintf(rbuf,sizeof(rbuf),"response '%s'", p);
		if(auth_send(rbuf))
			lostconn();
	}
	if(stin) {
		if(rbuf[2] != '\0')
			fprintf(stderr,"Logged in %s\n",&rbuf[2]);
		else
			fprintf(stderr,"Logged in\n");
	}
	logged_in = 1;
	return(0);
}


static	int
do_passwd(ac,av,original)
int	ac;
char	*av[];
char	*original;
{
	char		rbuf[MAX_STR];
	char		*p;
	char		*pwp1;
	char		*pwp2;
	char		pbuf[AUTH_PWSIZ];
	char		pxuf1[MAX_STR];
	char		pxuf2[MAX_STR];

	if(!logged_in) {
		fprintf(stderr,"Log in, first\n");
		return(0);
	}

	pwp1 = "Password:";
	pwp2 = "Repeat Password:";
	if(ac > 1) {
		snprintf(pxuf1,sizeof(pxuf1),"Password for %.24s:",av[1]);
		snprintf(pxuf2,sizeof(pxuf2),"Repeat Password for %.24s:",av[1]);
		pwp1 = pxuf1;
		pwp2 = pxuf2;
	}
	if(ac > 2) 
		strlcpy(pbuf,av[2],sizeof(pbuf));
	else {
		if((p = getpassword(pwp1)) == (char *)0) {
			fprintf(stderr,"Password not changed\n");
			return(0);
		}
		strlcpy(pbuf,p, sizeof(pbuf));
		if((p = getpassword(pwp2)) == (char *)0) {
			fprintf(stderr,"Password not changed\n");
			return(0);
		}
		if(strcmp(p,pbuf)) {
			fprintf(stderr,"Passwords differ - not changed\n");
			return(0);
		}
	}

	if(index(pbuf,'"') != (char *)0 || index(pbuf,'\'') != (char *)0) {
		fprintf(stderr,"Passwords cannot contain quotes - not changed\n");
		return(0);
	}

	if(ac > 1)
		snprintf(rbuf,sizeof(rbuf),"password %.24s \"%.50s\"\n",av[1],pbuf);
	else
		snprintf(rbuf,sizeof(rbuf),"password \"%.50s\"\n",pbuf);

	if(auth_send(rbuf))
		lostconn();
	if(auth_recv(rbuf,sizeof(rbuf)))
		lostconn();
	fprintf(stderr,"%s\n",rbuf);
	return(0);
}


/* send a whole line over intact and expect back a multiline response */
static	int
do_multiline(ac,av,original)
int	ac;
char	*av[];
char	*original;
{
	char		rbuf[MAX_STR];

	if(auth_send(original))
		lostconn();
	while(1) {
		if(auth_recv(rbuf,sizeof(rbuf)))
			lostconn();
		if(rbuf[0] == '.' && rbuf[1] == '\0')
			break;
		if(rbuf[0] == '.')
			fprintf(stderr,"%s\n",&rbuf[1]);
		else
			fprintf(stderr,"%s\n",rbuf);
	}
	return(0);
}



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



