/*************************************************** */
/* Rule Set Based Access Control                     */
/*                                                   */
/* Author and (c) 1999-2019: Amon Ott <ao@rsbac.org> */
/*                                                   */
/* Last modified: 29/Nov/2019                        */
/*************************************************** */

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <grp.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rsbac/types.h>
#include <rsbac/syscalls.h>
#include <rsbac/error.h>
#include "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define ROOM 20

int main(int argc, char ** argv)
{
	int res = 0;
	char * progname;
	rsbac_uid_t user = RSBAC_GEN_UID(RSBAC_UM_VIRTUAL_KEEP, RSBAC_NO_USER);
	char hostname[RSBAC_MAXNAMELEN + 1];
	int verbose = 0;
	int preserve = 0;
	int err;
	char * pass;
	char * username;
	union rsbac_um_mod_data_t um_data;
	rsbac_gid_num_t * group_array;
	struct termios old_term;
	struct termios tmp_term;

	locale_init();
	progname = argv[0];

	while((argc > 1) && (argv[1][0] == '-'))
	{
		char * pos = argv[1];
		pos++;
		if(*pos == '-')
		{
			argv++;
			argc--;
			break;
		}
		while(*pos)
		{
			switch(*pos)
			{
				case 'v':
					verbose++;
					break;

				case 'p':
					preserve = 1;
					break;

				case 'h':
					printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
					printf(gettext("Use: %s [flags] [username]\n"), progname);
					printf(gettext("  -v = verbose, -p = preserve environment\n"));
					exit(0);

				default:
					fprintf(stderr, gettext("%s: unknown parameter %c\n"), progname, *pos);
					exit(1);
			}
			pos++;
		}
		argv++;
		argc--;
	}
	err = gethostname(hostname, RSBAC_MAXNAMELEN);
	error_exit(err);
	if (argc > 1)
	{
		username = argv[1];
	}
	else
	{
		username = malloc(255);
		if(!username)
			error_exit(-ENOMEM);
		printf("%s login: ", hostname);
		if(scanf("%254s", username) <= 0)
		{
			fprintf(stderr, gettext("%s: invalid login name!\n"), progname);
			exit(1);
		}
	}

	pass = malloc(255);
	if(!pass)
		error_exit(-ENOMEM);
	res = mlock(pass, 255);
	if (res) {
		fprintf(stderr, gettext("Unable to lock password into physical memory, continue anyway!\n"));
	}
	printf("%s's RSBAC password: ", username);
	if(isatty(STDIN_FILENO))
	{
		res = tcgetattr(STDIN_FILENO, &old_term);
		error_exit(res);
		memcpy(&tmp_term, &old_term, sizeof(old_term));
		tmp_term.c_lflag &= ~(ECHO);
		res = tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp_term);
		error_exit(res);
	}
	res = scanf("%254s", pass);
	if(isatty(STDIN_FILENO))
		tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
	printf("\n");
	if((rsbac_um_auth_name(username, pass) != 0) || (res <=0)) {
		memset(pass, 0, 255);
		munlock(pass, 255);
		fprintf(stderr, gettext("Login incorrect\n"));
		exit(1);
	}
	memset(pass, 0, 255);
	munlock(pass, 255);
	res = rsbac_um_get_uid(0, username, &user);
//	printf("Uid: %u/%u\n", RSBAC_UID_SET(user), RSBAC_UID_NUM(user));
	error_exit(res);
	res = rsbac_um_get_user_item(0, user, UM_group, &um_data);
	error_exit(res);
	res = setgid(um_data.group);
	error_exit(res);
	res = rsbac_um_get_gm_list(0, user, NULL, 0);
	error_exit(res);
	if(res > 0)
	{
		res += ROOM;
		group_array = malloc(res * sizeof(*group_array));
		if(!group_array)
			error_exit(-RSBAC_ENOMEM);
		res = rsbac_um_get_gm_list(0, user, group_array, res);
		if(res > 0)
			res = setgroups(res, group_array);
		free(group_array);
		error_exit(res);
	}
	res = rsbac_um_get_user_item(0, user, UM_homedir, &um_data);
	error_exit(res);
	res = setuid(RSBAC_UID_NUM(user));
	error_exit(res);
	res = chdir(um_data.string);
	if(res) {
		fprintf(stderr, "Could not chdir to home dir %s: ", um_data.string);
		show_error(res);
	}
	res = rsbac_um_get_user_item(0, user, UM_shell, &um_data);
	error_exit(res);
	{
		char * execargs[2];
		char arg0[RSBAC_MAXNAMELEN + 10];

		if(!preserve) {
			char * termvar;

			termvar = getenv("TERM");
			if(termvar) {
				strncpy(arg0, termvar, RSBAC_MAXNAMELEN + 9);
				arg0[RSBAC_MAXNAMELEN - 1] = 0;
			}
			clearenv();
			if(termvar)
				setenv("TERM", arg0, 0);
		}

		/* always to be set (although we have excluded MAIL)*/
		res = rsbac_um_get_user_item(0, user, UM_name, &um_data);
		setenv("LOGNAME", um_data.string, 1);
		res = rsbac_um_get_user_item(0, user, UM_homedir, &um_data);
		setenv("HOME", um_data.string, 1);
		res = rsbac_um_get_user_item(0, user, UM_shell, &um_data);
		setenv("SHELL", um_data.string, 1);

		if(user == 0)
			putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin");
		else
			putenv("PATH=/bin:/usr/bin");

		snprintf(arg0, RSBAC_MAXNAMELEN+2, "-%s", um_data.string);
		execargs[0] = arg0;
		execargs[1] = NULL;
		res = execv(um_data.string, execargs);
	}
	error_exit(res);
	exit(0);
}
