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

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

char * progname;
int verbose = 0;
int backup = 0;
int showpass = 0;
int printdates = 0;
rsbac_list_ta_number_t ta_number = 0;
const char   add_prog[] = "rsbac_useradd";
const char   mod_prog[] = "rsbac_usermod";

rsbac_um_set_t vset = RSBAC_UM_VIRTUAL_KEEP;

#define ROOM 20

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] username\n"), progname);
      printf(gettext(" -v = verbose, -a = list all users\n"));
      printf(gettext(" -l = short list all users, -b = backup mode\n"));
      printf(gettext(" -p = also show encrypted password\n"));
      printf(gettext(" -D = print dates as yyyymmdd, not day number\n"));
      printf(gettext(" -u = list calling user\n"));
      printf(gettext(" -S n = virtual user set n\n"));
      printf(gettext(" -N ta = transaction number (default = value of RSBAC_TA, if set, or 0)\n"));
    }

void pass_print(__u8 * pass, u_int len)
  {
    u_int i;

    for(i=0; i<len; i++)
      {
        printf("%02x", pass[i]);
      }
  }

int process(rsbac_uid_t user)
  {
      union rsbac_um_mod_data_t data;
      int res;
      char username[RSBAC_MAXNAMELEN];

      res = rsbac_um_get_user_item(ta_number, user, UM_name, &data);
      if(!res)
        {
          strcpy(username, data.string);
          if(backup)
            {
              if (RSBAC_UID_SET(user) != RSBAC_UM_VIRTUAL_KEEP)
                printf("%s -S %u -u %u %s\n",
                       add_prog, RSBAC_UID_SET(user), RSBAC_UID_NUM(user), username);
              else
                printf("%s -u %u %s\n", add_prog, RSBAC_UID_NUM(user), username);
            }
          else
            {
              printf("\n");
              if (RSBAC_UID_SET(user) != RSBAC_UM_VIRTUAL_KEEP)
                printf("Virtual Set: %u\n", RSBAC_UID_SET(user));
              printf("Uid: %u\n", RSBAC_UID_NUM(user));
              printf("Name: %s\n", data.string);
            }
        }
      else
        {
          if (RSBAC_UID_SET(user) != RSBAC_UM_VIRTUAL_KEEP)
            fprintf(stderr, gettext("%s: Unknown user %u/%u\n"),
                    progname, RSBAC_UID_SET(user), RSBAC_UID_NUM(user));
          else
            fprintf(stderr, gettext("%s: Unknown user %u\n"),
                    progname, RSBAC_UID_NUM(user));
          exit(1);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_fullname, &data);
      if(!res)
        {
          if(backup) {
            if (RSBAC_UID_SET(user) != RSBAC_UM_VIRTUAL_KEEP)
              printf("%s -S %u -c \"%s\"", mod_prog, RSBAC_UID_SET(user), data.string);
            else
              printf("%s -c \"%s\"", mod_prog, data.string);
          }
          else
            printf("Fullname: %s\n", data.string);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_shell, &data);
      if(!res)
        {
          if(backup)
            printf(" -s \"%s\"", data.string);
          else
            printf("Shell: %s\n", data.string);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_homedir, &data);
      if(!res)
        {
          if(backup)
            printf(" -d \"%s\"", data.string);
          else
            printf("Homedir: %s\n", data.string);
        }
      if(showpass)
        {
          res = mlock(&data, RSBAC_UM_PASS_LEN);
	  if (res) {
		  fprintf(stderr, gettext("Unable to lock password into physical memory, continue anyway!\n"));
	  }
	  res = rsbac_um_get_user_item(ta_number, user, UM_pass, &data);
          if(!res)
            {
              if(backup)
                {
                  printf(" -Q ");
                  pass_print(data.string, RSBAC_UM_PASS_LEN);
                }
              else
                {
                  printf("Password: ");
                  pass_print(data.string, RSBAC_UM_PASS_LEN);
                  printf("\n");
                }
            }
	  memset(&data, 0, RSBAC_UM_PASS_LEN);
	  munlock(&data, RSBAC_UM_PASS_LEN);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_group, &data);
      if(!res)
        {
          if(backup)
            printf(" -g %u", data.group);
          else
            printf("Group: %u\n", data.group);
        }
      {
        rsbac_gid_num_t * group_array = NULL;
        int group_num = 0;
        int i;

        group_num = rsbac_um_get_gm_list(ta_number, user, NULL, 0);
        if(group_num > 0)
          {
            group_num += ROOM;
            group_array = malloc(group_num * sizeof(*group_array));
	    if(!group_array)
              {
                error_exit(-RSBAC_ENOMEM);
              }
            group_num = rsbac_um_get_gm_list(ta_number, user, group_array, group_num);
            if(group_num > 0)
              {
                if(backup)
                  {
                    printf(" -G");
                    for(i=0; i<group_num; i++)
                      if(!i)
                        printf(" %u", group_array[i]);
                      else
                        printf(",%u", group_array[i]);
                  }
                else
                  {
                    printf("More groups:");
                    for(i=0; i<group_num; i++)
                      if(!i)
                        printf(" %u", group_array[i]);
                      else
                        printf(",%u", group_array[i]);
                    printf("\n");
                  }
              }
            free(group_array);
          }
      }

      res = rsbac_um_get_user_item(ta_number, user, UM_lastchange, &data);
      if(!res)
        {
          if(backup)
            printf(" -l %u", data.days);
          else
          if(printdates)
            {
              struct tm * tm_p;
              time_t secs;

              secs = data.days * 86400;
              
              tm_p = gmtime(&secs);
              if(tm_p)
                printf("Lastchange: %04u%02u%02u\n",
                       tm_p->tm_year + 1900,
                       tm_p->tm_mon + 1,
                       tm_p->tm_mday);
            }
          else
            printf("Lastchange: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_minchange, &data);
      if(!res)
        {
          if(backup)
            printf(" -n %u", data.days);
          else
            printf("Minchange: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_maxchange, &data);
      if(!res)
        {
          if(backup)
            printf(" -x %u", data.days);
          else
            printf("Maxchange: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_warnchange, &data);
      if(!res)
        {
          if(backup)
            printf(" -w %u", data.days);
          else
            printf("Warnchange: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_inactive, &data);
      if(!res)
        {
          if(backup)
            printf(" -f %i", data.days);
          else
            printf("Inactive: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_expire, &data);
      if(!res)
        {
          if(backup)
            printf(" -e %i", data.days);
          else
          if(printdates)
            {
              struct tm * tm_p;
              time_t secs;

              secs = data.days * 86400;
              
              tm_p = gmtime(&secs);
              if(tm_p)
                printf("Expire: %04u%02u%02u\n",
                       tm_p->tm_year + 1900,
                       tm_p->tm_mon + 1,
                       tm_p->tm_mday);
            }
          else
            printf("Expire: %u\n", data.days);
        }
      res = rsbac_um_get_user_item(ta_number, user, UM_ttl, &data);
      if(!res)
        {
          if(backup)
            {
              if(data.ttl)
                printf(" -T %lu", data.ttl + time(NULL));
            }
          else
            printf("Account TTL: %u\n", data.ttl);
        }

      if(backup)
        {
          printf(" %s\n", username);
        }
      return 0;
  }

int main(int argc, char ** argv)
{
  int res = 0;
  int i;
  int list = 0;

  locale_init();

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

    if(env)
      ta_number = strtoul(env,0,0);
  }

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

              case 'b':
                backup = 1;
                break;

              case 'p':
                showpass = 1;
                break;

              case 'D':
                printdates = 1;
                break;

              case 'u':
                process(RSBAC_GEN_UID(RSBAC_UM_VIRTUAL_KEEP,getuid()));
                exit(0);

              case 'l':
                list = 1;
                /* fall through */
              case 'a':
                {
                  rsbac_uid_t * user_array = NULL;
                  int user_num = 0;
                  int i;

                  user_num = rsbac_um_get_user_list(ta_number, vset, NULL, 0);
                  error_exit(user_num);
                  user_num += ROOM;
                  user_array = malloc(user_num * sizeof(*user_array));
	          if(!user_array)
	            {
                      error_exit(-RSBAC_ENOMEM);
	            }
                  user_num = rsbac_um_get_user_list(ta_number, vset, user_array, user_num);
                  error_exit(user_num);
                  if(user_num > 0)
                    {
                      qsort(user_array, user_num, sizeof(*user_array), rsbac_user_compare);
                      if(list)
                        {
                          union rsbac_um_mod_data_t data;

                          for(i=0; i<user_num; i++)
                            {
                              res = rsbac_um_get_user_item(ta_number, user_array[i], UM_name, &data);
                              if(!res) {
                                if (RSBAC_UID_SET(user_array[i]) > 0)
                                  printf("%u/%s %u\n",
                                       RSBAC_UID_SET(user_array[i]),
                                       data.string,
                                       RSBAC_UID_NUM(user_array[i]));
                                else
                                  printf("%s %u\n",
                                       data.string, RSBAC_UID_NUM(user_array[i]));
                              }
                            }
                        }
                      else
                        for(i=0; i<user_num; i++)
                          process(user_array[i]);
                    }
                  free(user_array);
                  exit(0);
                }
              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);
                    exit(1);
                  }
                break;
              case 'S':
                if(argc > 2)
                  {
                    if (rsbac_get_vset_num(argv[2], &vset))
                      {
                        fprintf(stderr, gettext("%s: invalid virtual set number for parameter %c\n"), progname, *pos);
                        exit(1);
                      }
                    argc--;
                    argv++;
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: missing virtual set number for parameter %c\n"), progname, *pos);
                    exit(1);
                  }
                break;

              default:
                fprintf(stderr, gettext("%s: unknown parameter %c\n"), progname, *pos);
                exit(1);
            }
          pos++;
        }
      argv++;
      argc--;
    }

  if (argc > 1)
    {
      for(i=1; i<argc; i++)
        {
          rsbac_uid_t user = RSBAC_GEN_UID(vset, RSBAC_NO_USER);
          if(rsbac_um_get_uid(ta_number, argv[i], &user))
            {
              char * tmp_name = argv[i];
              char * p = tmp_name;
              rsbac_um_set_t tmp_vset = vset;

              while (*p && (*p != '/'))
                p++;
              if (*p) {
                    *p = 0;
                    if (rsbac_get_vset_num(tmp_name, &tmp_vset))
                      {
                        fprintf(stderr, gettext("%s: invalid virtual set number %s, skipping\n"), tmp_name);
                        continue;
                      }
                    *p = '/';
                    p++;
                    tmp_name = p;
              }
              user = strtoul(tmp_name, NULL, 0);
              if(!user && strcmp(tmp_name,"0"))
                {
                  fprintf(stderr, gettext("%s: Unknown user %s\n"), progname, argv[i]);
                  return 1;
                }
              user = RSBAC_GEN_UID(tmp_vset, user);
            }
          process(user);
        }
      exit(0);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
