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

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.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

char * progname;
int verbose = 0;
int backup = 0;
int showpass = 0;
char addgidreplace[4] = "";
rsbac_list_ta_number_t ta_number = 0;
rsbac_um_set_t vset = RSBAC_UM_VIRTUAL_KEEP;

const char   add_prog[] = "rsbac_groupadd";
const char   mod_prog[] = "rsbac_groupmod";

#define ROOM 20

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] groupname\n"), progname);
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext(" -v = verbose\n"));
      printf(gettext(" -a = list all groups\n"));
      printf(gettext(" -l = short list all groups\n"));
      printf(gettext(" -b = backup mode\n"));
      printf(gettext(" -r = add -r flag to rsbac_groupadd (requires -b)\n"));
      printf(gettext(" -p = also show encrypted password\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_gid_t group)
  {
      union rsbac_um_mod_data_t data;
      int res;
      char groupname[RSBAC_MAXNAMELEN];

      res = rsbac_um_get_group_item(ta_number, group, UM_name, &data);
      if(!res)
        {
         if(backup)
           {
             strcpy(groupname, data.string);
             if (RSBAC_GID_SET(group) != RSBAC_UM_VIRTUAL_KEEP)
               printf("%s%s -S %u -g %u %s\n",
                      add_prog, addgidreplace, RSBAC_GID_SET(group), RSBAC_GID_NUM(group), groupname);
             else
               printf("%s%s -g %u %s\n", add_prog, addgidreplace,
                      RSBAC_GID_NUM(group), groupname);
           }
         else
           {
             printf("\n");
             if (RSBAC_GID_SET(group) != RSBAC_UM_VIRTUAL_KEEP)
               printf("Virtual Set: %u\n", RSBAC_GID_SET(group));
             printf("Gid: %u\n", RSBAC_GID_NUM(group));
             printf("Name: %s\n", data.string);
           }
        }
      else
        {
          if (RSBAC_GID_SET(group) != RSBAC_UM_VIRTUAL_KEEP)
            fprintf(stderr, gettext("%s: Unknown group %u/%u\n"),
                    progname, RSBAC_GID_SET(group), RSBAC_GID_NUM(group));
          else
            fprintf(stderr, gettext("%s: Unknown group %u\n"),
                    progname, RSBAC_GID_NUM(group));
          exit(1);
        }
      if(backup)
        {
          if (RSBAC_GID_SET(group) != RSBAC_UM_VIRTUAL_KEEP)
            printf("%s -S %u", mod_prog, RSBAC_GID_SET(group));
          else
            printf("%s", mod_prog);
        }
      if(showpass)
        {
          rsbac_boolean_t oldpass = FALSE;

          res = rsbac_um_get_group_item(ta_number, group, UM_pass_algo, &data);
          if (res || data.string[0] == RSBAC_UM_PW_HASH_OLD_DEFAULT_NUM) {
            if (res && verbose) {
              fprintf(stderr, gettext("rsbac_um_get_group_item for UM_pass_algo: fallback to UM_pass: "));
              show_error(res);
            }
            oldpass = TRUE;
            res = rsbac_um_get_group_item(ta_number, group, UM_pass, &data);
          }
          if(!res)
            {
              if(backup)
                {
                  printf(" -Q ");
                  pass_print((__u8 *) data.string, oldpass ? RSBAC_UM_PASS_LEN : RSBAC_UM_PWDATA_LEN);
                }
              else
                {
                  printf("Password: ");
                  pass_print((__u8 *) data.string, oldpass ? RSBAC_UM_PASS_LEN : RSBAC_UM_PWDATA_LEN);
                  printf("\n");
                }
            }
        }
      res = rsbac_um_get_group_item(ta_number, group, UM_ttl, &data);
      if(!res)
        {
          if(backup)
            {
              if(data.ttl)
                printf(" -T %lu", data.ttl + time(NULL));
            }
          else
            printf("Group TTL: %u\n", data.ttl);
        }
      if(backup)
        {
          printf(" %s\n", groupname);
        }
      else
        {
          rsbac_uid_num_t * user_array = NULL;
          int user_num = 0;
          int i;

          user_num = rsbac_um_get_gm_user_list(ta_number, group, NULL, 0);
          if(user_num > 0)
            {
              user_num += ROOM;
              user_array = malloc(user_num * sizeof(*user_array));
	      if(!user_array)
                {
                  error_exit(-RSBAC_ENOMEM);
                }
              user_num = rsbac_um_get_gm_user_list(ta_number, group, user_array, user_num);
              if(user_num > 0)
                {
                  qsort(user_array, user_num, sizeof(*user_array), rsbac_user_compare);
                  printf("Group extra members:");
                  for(i=0; i<user_num; i++)
                    if(!i)
                      printf(" %u", user_array[i]);
                    else
                      printf(",%u", user_array[i]);
                  printf("\n");
                }
              free(user_array);
            }
        }
      return 0;
  }

int main(int argc, char ** argv)
{
  int res = 0;
  rsbac_gid_t group;
  int i;
  int list = 0;
  u_int stopflags = FALSE;

  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 'v':
                verbose++;
                break;

              case 'b':
                backup = 1;
                break;
              case 'r':
                sprintf(addgidreplace, " -r");
                break;

              case 'p':
                showpass = 1;
                break;

              case 'l':
                list = 1;
                /* fall through */
              case 'a':
                {
                  rsbac_gid_t * group_array = NULL;
                  int group_num = 0;
                  int i;

                  group_num = rsbac_um_get_group_list(ta_number, vset, NULL, 0);
                  error_exit(group_num);
                  group_num += ROOM;
                  group_array = malloc(group_num * sizeof(*group_array));
	          if(!group_array)
	            {
                      error_exit(-RSBAC_ENOMEM);
	            }
                  group_num = rsbac_um_get_group_list(ta_number, vset, group_array, group_num);
                  error_exit(group_num);
                  if(group_num > 0)
                    {
                      qsort(group_array, group_num, sizeof(*group_array), rsbac_group_compare);
                      if(list)
                        {
                          union rsbac_um_mod_data_t data;

                          for(i=0; i<group_num; i++)
                            {
                              res = rsbac_um_get_group_item(ta_number, group_array[i], UM_name, &data);
                              if(!res) {
                                printf("%u %s\n",
                                       RSBAC_GID_NUM(group_array[i]), data.string);
                              }
                            }
                        }
                      else
                        for(i=0; i<group_num; i++)
                          process(group_array[i]);
                    }
                  free(group_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++)
        {
          group = RSBAC_GEN_GID(vset, RSBAC_NO_GROUP);
          if(rsbac_um_get_gid(ta_number, argv[i], &group))
            {
              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"), progname, tmp_name);
                        continue;
                      }
                    *p = '/';
                    p++;
                    tmp_name = p;
              }
              group = strtoul(tmp_name, NULL, 0);
              if(!group && strcmp(tmp_name,"0"))
                {
                  fprintf(stderr, gettext("%s: Unknown group %s\n"), progname, argv[i]);
                  return 1;
                }
              group = RSBAC_GEN_GID(tmp_vset, group);
            }
          process(group);
        }
      exit(0);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
