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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <rsbac/types.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/getname.h>
#include <rsbac/cap_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 "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

char * progname;

void use(void)
  {
        printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
        printf(gettext("Use: %s module target-type file/dirname attribute [request]\n"), progname);  
        printf(gettext("Use: %s module target-type file/dirname attribute [position]\n"), progname);  
        printf(gettext("Use: %s list_category_nr\n"), progname);  
        printf(gettext(" -h = this help, -- = no more flags,\n"));
        printf(gettext(" -e = show effective (maybe inherited) value, not real\n"));
        printf(gettext(" -d = numeric device specification ({b|c}major[:minor])\n"));
        printf(gettext(" -p = print requests, -n [target] = list all requests [for target]\n"));
        printf(gettext(" -c list all Linux capabilities, -R = list all RES resource names\n"));
        printf(gettext(" -C path = convert path to device special file to device specification\n"));
        printf(gettext(" -a = list attributes and values\n"));
        printf(gettext(" -N ta = transaction number (default = value of RSBAC_TA, if set, or 0)\n"));
        printf(gettext(" module = GEN, MAC, FC, SIM, PM, MS, FF, RC, AUTH, RES or PAX\n"));
        printf(gettext(" target-type = FILE, DIR, FIFO, SYMLINK or DEV\n"));
  }

int main(int argc, char ** argv)
{
  enum rsbac_attribute_t attr_list[RSBAC_FD_NR_ATTRIBUTES] = RSBAC_FD_ATTR_LIST;
  enum rsbac_attribute_t attr_list_dev[RSBAC_DEV_NR_ATTRIBUTES] = RSBAC_DEV_ATTR_LIST;
  int res = 0;
  char tmp1[RSBAC_MAXNAMELEN],tmp2[RSBAC_MAXNAMELEN],tmp3[RSBAC_MAXNAMELEN];
  int j;
  union rsbac_attribute_value_t value,value2;
  enum rsbac_switch_target_t module = SW_NONE;
  enum rsbac_target_t target;
  enum rsbac_attribute_t attr;
  enum rsbac_adf_request_t request;
  enum rsbac_log_level_t   log_level;
  int inherit = 0;
  u_int position;
  int verbose = 0;
  int printall = 0;
  int scripting = 0;
  int numdev = 0;
  rsbac_list_ta_number_t ta_number = 0;
  u_int stopflags = FALSE;

  progname = argv[0];
  locale_init();
  {
    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 'p':
                printall=1;
                break;
              case 'e':
                inherit=1;
                break;
              case 'd':
                numdev=1;
                break;
              case 's':
                scripting=1;
                break;
              case 'n':
                {
                  int i;
                  rsbac_request_vector_t rvector = -1;

                  if(   (argc > 2)
                     && ((target = get_target_nr(argv[2])) != T_NONE)
                    )
                    {
                      switch(target)
                        {
                          case T_FILE:
                          case T_DIR:
                          case T_FIFO:
                          case T_SYMLINK:
                          case T_FD:
                            rvector = RSBAC_FD_REQUEST_VECTOR;
                            break;
                          case T_DEV:
                            rvector = RSBAC_DEV_REQUEST_VECTOR;
                            break;
                          case T_SCD:
                            rvector = RSBAC_SCD_REQUEST_VECTOR;
                            break;
                          default:
                            break;
                        }
                    }
                  for(i=0; i<R_NONE; i++)
                    {
                      if(rvector & RSBAC_REQUEST_VECTOR(i))
                        printf("%s\n", get_request_name(tmp1, i));
                    }
                  exit(0);
                }
              case 'c':
                {
                  char tmp[RSBAC_MAXNAMELEN];
                  int i;

                  for(i=0; i<CAP_NONE; i++)
                    printf("%s\n", get_cap_name(tmp, i));
                  exit(0);
                }
              case 'R':
                {
                  char tmp[RSBAC_MAXNAMELEN];
                  int i;

                  for(i=0; i<=RSBAC_RES_MAX; i++)
                    printf("%s\n", get_res_name(tmp, i));
                  exit(0);
                }
              case 'a':
              case 'A':
                if(   (argc > 2)
                   && ((attr = get_attribute_nr(argv[2])) != A_none)
                  )
                  {
                    get_switch_target_name(tmp1, get_attr_module(attr));
                    get_attribute_name(tmp2, attr);
                    get_attribute_param(tmp3, attr);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                    exit(0);
                  }
                printf(gettext("- attribute (string) and returned value = see following list:\n"));
                printf(gettext("- FILE, DIR, FIFO and SYMLINK:\n"));
                printf(gettext("log_level\t\t(additional parameter request-type)\n\t\t\t0=none, 1=denied, 2=full, 3=request based\n"));
                printf(gettext("mac_categories\t\t(with additional parameter position)\n\t\t\t0=no, 1=yes\n"));
                for (j=0;j<RSBAC_FD_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);
                  }
                printf(gettext("[RES ] res_min|res_max (with additional parameter position)\n\tnon-negative integer (0 for unset)\n"));
                printf("\n- DEV:\n");
                printf(gettext("log_level\t\t(additional parameter request-type)\n\t\t\t0=none, 1=denied, 2=full, 3=request based\n"));
                printf(gettext("mac_categories\t\t(with additional parameter position)\n\t\t\t0=no, 1=yes\n"));
                for (j=0;j<RSBAC_DEV_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(attr_list_dev[j]));
                    get_attribute_name(tmp2,attr_list_dev[j]);
                    get_attribute_param(tmp3,attr_list_dev[j]);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                  }
                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 'C':
                if(argc > 2)
                  {
                    struct stat buf;

                    res = stat(argv[2], &buf);
                    error_exit(res);
                    if(S_ISBLK(buf.st_mode))
                      {
                        printf("b%u:%u\n", major(buf.st_rdev), minor(buf.st_rdev));
                      }
                    else
                    if(S_ISCHR(buf.st_mode))
                      {
                        printf("c%u:%u\n", major(buf.st_rdev), minor(buf.st_rdev));
                      }
                    else
                      {
                        fprintf(stderr, gettext("%s: %s is no device special file\n"), progname, argv[2]);
                        exit(1);
                      }
                    exit(0);
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: missing path 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)
    {
      if(!strcmp(argv[1],"list_category_nr"))
        {
          for(j=0; j<RSBAC_MAC_NR_CATS; j++)
            printf("%i\n", j);
          exit(0);
        }
      module = get_switch_target_nr(argv[1]);
      if(module != SW_NONE)
        {
          argv++;
          argc--;
        }
    }
  switch(argc)
    {
      case 4:
        target = get_target_nr(argv[1]);
        attr = get_attribute_nr(argv[3]);
        value.dummy = -1;
        if(numdev)
          {
            union rsbac_target_id_t tid;

            error_exit(strtodevdesc(argv[2], &tid.dev));
            res = rsbac_get_attr(ta_number, module, target, &tid, attr, &value, inherit);
          }
        else
          res = rsbac_get_attr_n(ta_number, module, target, argv[2], attr, &value, inherit);
        error_exit(res);
        switch (attr)
          {
            case A_log_array_low:
            case A_log_array_high:
              printf("%s\n",u64tostrlog(tmp1,value.log_array_low));
              break;
            case A_log_program_based:
              if(printall)
                {
                  int i;

                  for (i=0; i<R_NONE; i++)
                    if(value.log_program_based & ((rsbac_request_vector_t) 1 << i))
                      printf(" %s\n", get_request_name(tmp1,i));
                }
              else
                printf("%s\n",u64tostrlog(tmp1,value.log_array_low));
              break;
            case A_mac_categories:
              printf("%s\n",u64tostrmac(tmp1,value.mac_categories));
              break;
            case A_ff_flags:
              printf("%u\n",value.ff_flags);
              break;
            case A_mac_auto:
            case A_mac_prop_trusted:
            case A_mac_file_flags:
            case A_pm_object_type:
            case A_daz_do_scan:
            case A_daz_scanner:
            case A_daz_scanned:
            case A_auth_may_setuid:
            case A_security_level:
            case A_symlink_add_remote_ip:
            case A_symlink_add_uid:
            case A_symlink_add_rc_role:
            case A_linux_dac_disable:
            case A_fake_root_uid:
	    case A_cap_ld_env:
              printf("%u\n",value.u_char_dummy);
              break;
            case A_rc_type:
            case A_rc_type_fd:
            case A_rc_force_role:
            case A_rc_initial_role:
            case A_rc_role:
            case A_rc_def_role:
              printf("%u\n",value.rc_type);
              break;
            case A_min_caps:
            case A_max_caps:
              if(printall)
                {
                  int i;

                  for (i=0; i<32; i++)
                    if(value.min_caps.cap[0] & ((__u32) 1 << i))
                      printf(" %s\n", get_cap_name(tmp1,i));
                  for (i=32; i<CAP_NONE; i++)
                    if(value.min_caps.cap[1] & ((__u32) 1 << (i - 32)))
                      printf(" %s\n", get_cap_name(tmp1,i));
                }
              else
                {
                  kcaptostrcap(tmp1,value.min_caps);
                  printf("%s\n", tmp1);
                }
              break;
            case A_res_min:
            case A_res_max:
                {
                  int i;

                  if(scripting)
                    {
                      for (i=0; i<=RSBAC_RES_MAX; i++)
                        printf("%u ", value.res_array[i]);
                      printf("\n");
                    }
                  else
                    for (i=0; i<=RSBAC_RES_MAX; i++)
                      printf("%s\t%u\n", get_res_name(tmp1,i), value.res_array[i]);
                }
              break;
            case A_pax_flags:
              pax_print_flags(tmp1,value.pax_flags);
              printf("%s\n", tmp1);
              break;
            case A_auid_exempt:
            case A_vset:
              printf("%u\n",value.u_dummy);
              break;
            default:
              printf("%i\n",value.dummy);
          }
        exit(0);

      case 5:
        if(!strcmp(argv[3],"log_level"))
          {
            target = get_target_nr(argv[1]);
            request = get_request_nr(argv[4]);
            if(request == R_NONE)
              {
                fprintf(stderr, gettext("Invalid request type %s\n"), argv[4]);
                for(j=0;j<R_NONE;j++)
                  {
                    printf("%s\n", get_request_name(tmp1,j));
                  }
                exit(1);
              }
            if(numdev)
              {
                union rsbac_target_id_t tid;

                error_exit(strtodevdesc(argv[2], &tid.dev));
                res = rsbac_get_attr(ta_number, module, target, &tid, A_log_array_low, &value, inherit);
                error_exit(res);
                res = rsbac_get_attr(ta_number, module, target, &tid, A_log_array_high, &value2, inherit);
                error_exit(res);
              }
            else
              {
                res = rsbac_get_attr_n(ta_number, module, target, argv[2], A_log_array_low, &value, inherit);
                error_exit(res);
                res = rsbac_get_attr_n(ta_number, module, target, argv[2], A_log_array_high, &value2, inherit);
                error_exit(res);
              }
            log_level =     ((value.log_array_low   >> request) & 1)
                        | ( ((value2.log_array_high >> request) & 1) << 1);
            printf("%i\n",log_level);
            exit(0);
          }
        else
        if(!strcmp(argv[3],"mac_categories"))
          {
            target = get_target_nr(argv[1]);
            position = strtol(argv[4],0,10);
            if(position > RSBAC_MAC_MAX_CAT)
              {
                fprintf(stderr, gettext("Invalid position counter %s\n"), argv[4]);
                exit(1);
              }
            if(numdev)
              {
                union rsbac_target_id_t tid;

                error_exit(strtodevdesc(argv[2], &tid.dev));
                res = rsbac_get_attr(ta_number, module, target, &tid, A_mac_categories, &value, inherit);
              }
            else
              res = rsbac_get_attr_n(ta_number, module, target, argv[2], A_mac_categories, &value, inherit);
            error_exit(res);
            printf("%u\n",
                   (u_int) (value.mac_categories >> position) & 1);
            exit(0);
          }
        else
        if(!strcmp(argv[3],"log_program_based"))
          {
            target = get_target_nr(argv[1]);
            request = get_request_nr(argv[4]);
            if(request == R_NONE)
              {
                fprintf(stderr, gettext("Invalid request type %s\n"), argv[4]);
                printf(gettext("Valid request types:\n"));
                for(j=0;j<R_NONE;j++)
                  {
                    printf("%s\n", get_request_name(tmp1,j));
                  }
                exit(1);
              }
            if(numdev)
              {
                union rsbac_target_id_t tid;

                error_exit(strtodevdesc(argv[2], &tid.dev));
                res = rsbac_get_attr(ta_number, module, target, &tid, A_log_program_based, &value, inherit);
              }
            else
              res = rsbac_get_attr_n(ta_number, module, target, argv[2], A_log_program_based, &value, inherit);
            error_exit(res);
            printf("%u\n",
                   (u_int) (value.log_program_based >> request) & 1);
            exit(0);
          }
        else
        if(   !strcmp(argv[3],"res_min")
           || !strcmp(argv[3],"res_max")
          )
          {
            target = get_target_nr(argv[1]);
            position = get_res_nr(argv[4]);
            if(position == RSBAC_RES_NONE)
              {
                position = strtol(argv[4],0,10);
                if(   (!position && strcmp(argv[4], "0"))
                   || (position > RSBAC_RES_MAX)
                  )
                  {
                    fprintf(stderr, gettext("Invalid position counter %s\n"), argv[4]);
                    exit(1);
                  }
              }
            if(!strcmp(argv[3],"res_min"))
              attr = A_res_min;
            else
              attr = A_res_max;
            res = rsbac_get_attr_n(ta_number, module, target, argv[2], attr, &value, inherit);
            error_exit(res);
            printf("%u\n",
                   value.res_array[position]);
            exit(0);
          }

      default:
        use();
        return 1;
      }
  exit(1);
}
