/*************************************************** */
/* 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 <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <rsbac/types.h>
#include <rsbac/getname.h>
#include <rsbac/res_getname.h>
#include <rsbac/pax_getname.h>
#include <rsbac/syscalls.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include <rsbac/aci_data_structures.h>
#include "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define ROOM 10

const char   set_prog[] = "attr_set_user";
__s64 attr_list[RSBAC_USER_NR_ATTRIBUTES] = RSBAC_USER_ATTR_LIST;
int alluser = 0;
int verbose = 0;
int printall = 0;
int numeric = 0;
int nonumeric = 0;
rsbac_um_set_t vset = RSBAC_UM_VIRTUAL_ALL;
rsbac_list_ta_number_t ta_number = 0;
FILE * tfile;
char * filename = NULL;
union rsbac_target_id_t       tid;
union rsbac_attribute_value_t value;

rsbac_res_array_t res_min_def;
rsbac_res_array_t res_max_def;
rsbac_boolean_t res_usable = FALSE;
char * progname;
rsbac_boolean_t module_enabled[SW_NONE+1];

int def_attr[RSBAC_USER_NR_ATTRIBUTES] = {
      0, /* pseudo */
      0, /* log_user_based */
      SL_unclassified, /* security_level */
      SL_unclassified, /* initial_security_level */
      SL_unclassified, /* min_security_level */
      RSBAC_MAC_DEF_CAT_VECTOR, /* mac_categories */
      RSBAC_MAC_DEF_CAT_VECTOR, /* mac_initial_categories */
      RSBAC_MAC_MIN_CAT_VECTOR, /* mac_min_categories */
      SR_user, /* mac_role */
      RSBAC_MAC_DEF_U_FLAGS, /* mac_user_flags */
      SR_user, /* daz_role */
      SR_user, /* ff_role */
      SR_user, /* auth_role */
      RSBAC_RC_GENERAL_ROLE, /* rc_def_role */
      RSBAC_RC_GENERAL_TYPE, /* rc_type */
      0, /* min_caps */
      (__u32) -1, /* max_caps */
      SR_user, /* cap_role */
      LD_keep, /* cap_ld_env */
      SR_user, /* jail_role */
      SR_user, /* res_role */
      SR_user, /* pax_role */
      SR_user, /* UDF_role */
  };

void use()
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] [username(s)]\n"), progname);  
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext(" -a = process all users, -v = verbose,\n"));
      printf(gettext(" -p = print requests, -n = show numeric uid not username,\n"));
      printf(gettext(" -z = do not also try numeric username as uid\n"));
      printf(gettext(" -o target-file = write to file, not stdout,\n"));
      printf(gettext(" -A = list attributes and values,\n"));
      printf(gettext(" -M module = only backup attributes for this module,\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"));
    }

int process(rsbac_uid_t user, char * name)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN],tmp2[RSBAC_MAXNAMELEN];
    char intname[RSBAC_MAXNAMELEN];
    int j;
    enum  rsbac_switch_target_t module;

    tid.user = user;
    if(verbose)
      {
        if(name)
          printf(gettext("# Processing user %s\n"), name);
        else {
          if (RSBAC_UID_SET(user))
            printf(gettext("# Processing user %u/%u\n"),
                   RSBAC_UID_SET(user), RSBAC_UID_NUM(user));
          else
            printf(gettext("# Processing user %u\n"), RSBAC_UID_NUM(user));
        }
      }
    if(numeric || !name) {
      if (RSBAC_UID_SET(user))
        sprintf(intname, "%u/%u", RSBAC_UID_SET(user), RSBAC_UID_NUM(user));
      else
        sprintf(intname, "%u", RSBAC_UID_NUM(user));
    } else
      strcpy(intname,name);
    for (j=0;j < RSBAC_USER_NR_ATTRIBUTES;j++)
      {
        module = get_attr_module(attr_list[j]);
        if (!module_enabled[module])
          continue;
        value.dummy = -1;
        res = rsbac_get_attr(ta_number, module, T_USER, &tid, attr_list[j], &value, 0);
        if(res)
          {
            if(errno == RSBAC_EINVALIDMODULE)
              module_enabled[module] = FALSE;
            if(   (errno != RSBAC_EINVALIDMODULE)
               && (   verbose
                   || (errno != RSBAC_EINVALIDTARGET)
                  )
              )
              {
                get_error_name(tmp1,res);
                get_attribute_name(tmp2,attr_list[j]);
                fprintf(stderr, "%s (%s): %s\n",
                        intname, tmp2, tmp1);
                          
              }
          }
        else
          {
            switch(attr_list[j])
              {
                case A_rc_def_role:
                  if(value.rc_role != def_attr[j])
                    fprintf(tfile,
                            "%s -V %u %s %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.rc_role);
                  break;
                case A_pseudo:
                  if(value.pseudo != def_attr[j])
                    fprintf(tfile,
                            "%s -V %u %s %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.pseudo);
                  break;
                case A_security_level:
                case A_initial_security_level:
                case A_min_security_level:
                case A_mac_role:
                case A_mac_user_flags:
                case A_udf_role:
                case A_daz_role:
                case A_ff_role:
                case A_auth_role:
                case A_cap_role:
                case A_jail_role:
                case A_res_role:
                case A_pax_role:
                case A_cap_ld_env:
                  if(value.u_char_dummy != def_attr[j])
                    fprintf(tfile,
                            "%s -V %u %s %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.u_char_dummy);
                  break;
                case A_log_user_based:
                  if (value.log_user_based & RSBAC_ALL_REQUEST_VECTOR)
                    fprintf(tfile,
                            "%s -V %u %s %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            u64tostrlog(tmp2,value.log_user_based));
                  break;
              case A_max_caps:
              case A_min_caps:
                  if ((value.max_caps.cap[0] != def_attr[j]) || (value.max_caps.cap[1] != def_attr[j]))
                    {
                      if (printall)
                        {
                          int i;

                          fprintf(tfile,
                            "%s -V %u %s %s",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]));
                          for (i=0; i<32; i++)
                            if(value.min_caps.cap[0] & ((__u32) 1 << i))
                              fprintf(tfile, " %s", get_cap_name(tmp1,i));
                          for (i=32; i<CAP_NONE; i++)
                            if(value.min_caps.cap[1] & ((__u32) 1 << (i - 32)))
                              fprintf(tfile, " %s", get_cap_name(tmp1,i));
                          fprintf(tfile, "\n");
                        }
                      else
                        {
                          fprintf(tfile,
                            "%s -V %u %s %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            kcaptostrcap(tmp2, value.max_caps));
                        }
                    }
                break;
              case A_mac_categories:
              case A_mac_initial_categories:
                if (value.mac_categories != RSBAC_MAC_DEF_CAT_VECTOR)
                    fprintf(tfile,
                            "%s -V %u %s %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            u64tostrmac(tmp2,value.mac_categories));
                break;
              case A_mac_min_categories:
                if (value.mac_categories != RSBAC_MAC_MIN_CAT_VECTOR)
                    fprintf(tfile,
                            "%s -V %u %s %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            u64tostrmac(tmp2,value.mac_categories));
                break;

              default:
                  if(value.dummy != def_attr[j])
                    fprintf(tfile,
                            "%s -V %u %s %s %i\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            intname,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.dummy);
              }
          }
      }
    if(module_enabled[SW_RES] && res_usable)
      {
        res = rsbac_get_attr(ta_number, SW_RES, T_USER, &tid, A_res_min, &value, 0);
        if(!res)
          {
            for(j=0; j<= RSBAC_RES_MAX; j++)
              {
                if(value.res_array[j] != res_min_def[j])
                  fprintf(tfile,
                          "%s -V %u %s res_min %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          intname,
                          get_res_name(tmp1, j),
                          value.res_array[j]);
              }
            res = rsbac_get_attr(ta_number, SW_RES, T_USER, &tid, A_res_max, &value, 0);
            if(!res)
              {
                for(j=0; j<= RSBAC_RES_MAX; j++)
                  {
                    if(value.res_array[j] != res_max_def[j])
                      fprintf(tfile,
                              "%s -V %u %s res_max %s %u\n",
                              set_prog,
                              RSBAC_VERSION_NR,
                              intname,
                              get_res_name(tmp1, j),
                              value.res_array[j]);
                  }
              }
          }
      }
    return(res);
  }

int main(int argc, char ** argv)
{
  int res = 0;
  char tmp1[RSBAC_MAXNAMELEN],tmp2[RSBAC_MAXNAMELEN],tmp3[RSBAC_MAXNAMELEN];
  rsbac_uid_t user;
  int i,j;
  FILE * listfile = NULL;
  char * filelistname = NULL;
  u_int stopflags = FALSE;

  locale_init();

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

    if(env)
      ta_number = strtoul(env,0,0);
  }
  for (i=0; i<=SW_NONE; i++)
    module_enabled[i] = TRUE;
  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 'p':
                printall=1;
                break;
              case 'a':
                alluser=1;
                break;
              case 'n':
                numeric=1;
                break;
              case 'z':
                nonumeric=1;
                break;
              case 'o':
                if(argc > 2)
                  {
                    filename = argv[2];
                    argv++;
                    argc--;
                  }
                else
                  fprintf(stderr, gettext("%s: missing filename for parameter o\n"), progname);
                break;
              case 'T':
                if(argc > 2)
                  {
                    filelistname = argv[2];
                    argv++;
                    argc--;
                  }
                else
                  fprintf(stderr, gettext("%s: missing filename for parameter %c\n"), progname, *pos);
                break;
              case 'M':
                if(argc > 2)
                  {
                    enum rsbac_switch_target_t backup_module;

                    backup_module = get_switch_target_nr(argv[2]);
                    if (backup_module >= SW_NONE)
                      {
                        fprintf(stderr, gettext("%s: invalid module name %s for parameter %c\n"), progname, argv[2], *pos);
                        exit(1);
                      }
                    argc--;
                    argv++;
                    if (module_enabled[SW_NONE] == TRUE)
                      {
                        for (i=0; i<=SW_NONE; i++)
                          module_enabled[i] = FALSE;
                      }
                    module_enabled[backup_module] = TRUE;
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: missing module name for parameter %c\n"), progname, *pos);
                    exit(1);
                  }
                break;
              case 'A':
                printf(gettext("- attributes and values in backup = see following list:\n"));
                for (j=0;j<RSBAC_USER_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(attr_list[j]));
                    get_attribute_name(tmp2,attr_list[j]);
                    get_attribute_param(tmp3,attr_list[j]);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                  }
                exit(0);
              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;
              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;
              default:
                fprintf(stderr, gettext("%s: unknown parameter %c\n"), progname, *pos);
                exit(1);
            }
          pos++;
        }
      argv++;
      argc--;
    }

  if (   (argc > 1)
      || (alluser)
      || filelistname
     )
    {
      if(!filename)
        tfile = stdout;
      else
        {
          if (!(tfile=fopen(filename,"w")))
            {
              fprintf(stderr, gettext("opening target file returned error: %s\n"),
                              strerror(errno));
            }
        }
      tid.user = RSBAC_ALL_USERS;
      res = rsbac_get_attr(ta_number, SW_RES, T_USER, &tid, A_res_min, &value, 0);
      if(!res)
        {
          for(i=0; i<= RSBAC_RES_MAX; i++)
            {
              res_min_def[i] = value.res_array[i];
              if(   alluser
                 && res_min_def[i]
                )
                fprintf(tfile,
                        "%s -V %u %u res_min %s %u\n",
                        set_prog,
                        RSBAC_VERSION_NR,
                        RSBAC_ALL_USERS,
                        get_res_name(tmp1, i),
                        res_min_def[i]);
            }
          res = rsbac_get_attr(ta_number, SW_RES, T_USER, &tid, A_res_max, &value, 0);
          if(!res)
            {
              for(i=0; i<= RSBAC_RES_MAX; i++)
                {
                  res_max_def[i] = value.res_array[i];
                  if(   alluser
                     && res_max_def[i]
                    )
                    fprintf(tfile,
                            "%s -V %u %u res_max %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            RSBAC_ALL_USERS,
                            get_res_name(tmp1, i),
                            res_max_def[i]);
                }
              res_usable = TRUE;
            }
        }
      if(alluser)
        {
          int count;
          rsbac_uid_t * id_array;

          if(verbose)
            printf(gettext("# %s: processing all users\n"), progname);
          count = rsbac_list_all_user(ta_number, NULL, 0);
          error_exit(count);
          count += ROOM;
          id_array = malloc(count * sizeof(*id_array));
          if(!id_array)
            error_exit(-ENOMEM);
          count = rsbac_list_all_user(ta_number, id_array, count);
          if(verbose)
            printf(gettext("# %s: %i targets\n"), progname, count);
          if(count > 0)
            {
              qsort(id_array, count, sizeof(*id_array), rsbac_user_compare);
              for(i=0; i < count ; i++)
                {
                  if (   (vset == RSBAC_UM_VIRTUAL_ALL)
                      || (vset == RSBAC_UID_SET(id_array[i]))
                     )
                    {
                      if(get_user_name(ta_number, id_array[i], tmp1))
                        process(id_array[i], tmp1);
                      else
                        process(id_array[i], NULL);
                    }
                }
            }
        }
      else
        {
          if(filelistname)
            {
              if(!strcmp(filelistname, "-"))
                listfile = stdin;
              else
                if (!(listfile=fopen(filelistname,"r")))
                  {
                    fprintf(stderr, gettext("opening target list file returned error: %s\n"),
                            strerror(errno));
                    exit(1);
                  }
            }
          if(verbose)
            {
              printf(gettext("# %s: %i targets\n"), progname, argc - 2);
              if(filelistname)
                printf(gettext("# - plus targets from file %s\n"), filelistname);
            }
          for (i=1;i < argc;i++)
            {
              if(rsbac_get_uid_name(ta_number, &user, tmp1, argv[i], nonumeric))
                {
                  fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                          progname, argv[i]);
                }
              else
                process(user, tmp1);
            }
          if(filelistname)
            {
              char item[4096];
              int last;

              while(fgets(item, 4095, listfile))
                {
                  if(!*item)
                    continue;
                  last = strlen(item) - 1;
                  if(item[last] == '\n')
                    item[last] = 0;
                  if(*item)
                    {
                      if(rsbac_get_uid_name(ta_number, &user, tmp1, item, nonumeric))
                        {
                          fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                                  progname, item);
                        }
                      else
                        process(user, tmp1);
                    }
                }
              fclose(listfile);
            }
        }
      if(tfile != stdout)
        fclose(tfile);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
