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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/kdev_t.h>
#include <grp.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <rsbac/types.h>
#include <rsbac/syscalls.h>
#include <rsbac/getname.h>
#include <rsbac/error.h>
#include <termios.h>
#include <unistd.h>

#include "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

char * progname;
struct termios old_term;
struct termios tmp_term;


void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] {begin|refresh|commit|forget}\n"), progname);
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext("  -v = verbose, -b = print bash export of RSBAC_TA\n"));
      printf(gettext("  -t ttl = reduce transaction timeout from kernel config default to ttl\n"));
      printf(gettext("  -n name = set this transaction name\n"));
      printf(gettext("  -z = do not also try numeric username as uid\n"));
      printf(gettext("  -p password = use this password\n"));
      printf(gettext("  -P = ask for password\n"));
      printf(gettext("  -N ta = transaction number (for refresh, commit, forget)\n"));
      printf(gettext("          (default = value of RSBAC_TA, if set, or 0 otherwise)\n"));
    }


int env_init(void)
{
	struct rlimit rlimit;

	rlimit.rlim_cur = 0;
	rlimit.rlim_max = 0;
	setrlimit(RLIMIT_CORE, &rlimit);
	return 0;
}

int main(int argc, char ** argv)
{
  int res = 0;
  int ttl = 0;
  int verbose = 0;
  int bashexport = 0;
  int ask_password = 0;
  char * name = NULL;
  char * password = NULL;
  rsbac_list_ta_number_t ta_number = 0;
  rsbac_uid_t commit_uid = RSBAC_ALL_USERS;
  u_int stopflags = FALSE;
  int nonumeric = 0;

  env_init();

  locale_init();

  progname = argv[0];
  {
    char * env = getenv("RSBAC_TA");

    if(env)
      ta_number = strtoul(env,0,0);
  }
  
  while((argc > 1) && (argv[1][0] == '-') && !stopflags)
    {
      char * pos = argv[1];
      pos++;
      while(*pos)
        {
          switch(*pos)
            {
              case '-':
                stopflags = TRUE;
                break;
              case 'h':
                use();
                return 0;
              case 'z':
                nonumeric=1;
                break;
              case 't':
                if(argc > 2)
                  {
                    ttl = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing ttl value for parameter %c\n"), progname, *pos);
                break;
              case 'n':
                if(argc > 2)
                  {
                    name = argv[2];
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing name for parameter %c\n"), progname, *pos);
                break;
              case 'p':
                if(argc > 2)
                  {
                    password = argv[2];
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing password for parameter %c\n"), progname, *pos);
                break;
              case 'P':
				ask_password = 1;
				break;
              case 'u':
                if(argc > 2)
                  {
                    if(rsbac_get_uid(ta_number, &commit_uid, argv[2], nonumeric))
                      {
                        fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                                progname, argv[2]);
                        exit(1);
                      }
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing user for parameter %c\n"), progname, *pos);
                break;
              case 'N':
                if(argc > 2)
                  {
                    ta_number = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing transaction number value for parameter %c\n"), progname, *pos);
                break;
              case 'v':
                verbose++;
                break;
              case 'b':
                bashexport = 1;
                break;
              default:
                fprintf(stderr, gettext("%s: unknown parameter %c\n"), progname, *pos);
                exit(1);
            }
          pos++;
        }
      argv++;
      argc--;
    }

  if (ask_password) {
                  password = malloc(255);
                  if (!password)
                    exit(-ENOMEM);
                  res = mlock(password, 255);
                  if(res)
	            fprintf(stderr, gettext("Warning: password was not locked into physical memory.\n"));
		  printf("Password: ");
		  if (isatty(STDIN_FILENO)) {
				  tcgetattr(STDIN_FILENO, &old_term);
				  memcpy(&tmp_term, &old_term, sizeof(old_term));
				  tmp_term.c_lflag &= ~(ECHO);
				  tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp_term);
		  }
		  res = scanf("%254s", password);
		  if (isatty(STDIN_FILENO))
				  tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
		  printf("\n");
		  if (!res) {
				  fprintf(stderr, gettext("%s: invalid password!\n"), progname);
				  exit(1);
		  }

  }
  
  if (argc > 1)
    {
      if(!strcmp(argv[1], "begin"))
        {
          if (name)
            res = rsbac_list_ta_begin_name(ttl, &ta_number, commit_uid, name, password);
          else
            res = rsbac_list_ta_begin(ttl, &ta_number, commit_uid, password);
          if(!res)
            {
              if(bashexport)
                printf("export RSBAC_TA=%u\n", ta_number);
              else
                printf("%u\n", ta_number);
            }
        }
      else
      if(!strcmp(argv[1], "refresh"))
        res = rsbac_list_ta_refresh(ttl, ta_number, password);
      else
      if(!strcmp(argv[1], "commit"))
        res = rsbac_list_ta_commit(ta_number, password);
      else
      if(!strcmp(argv[1], "forget"))
        res = rsbac_list_ta_forget(ta_number, password);
      else
        {
          fprintf(stderr, "Invalid command %s!\n", argv[1]);
          exit(1);
        }
      error_exit(res);
      exit(0);
    }
  else
    {
      use();
      return 1;
    }
  exit(1);
}
