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

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <rsbac/types.h>
#include <rsbac/aci_data_structures.h>
#include <rsbac/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

 union rsbac_attribute_value_t value;
 enum rsbac_switch_target_t module;
 enum rsbac_target_t target;
 enum rsbac_attribute_t attr;
 char * progname;
int verbose=0;
int doremove=0;
int recurse=0;
rsbac_version_t version=RSBAC_VERSION_NR;
char * target_n;
char * attr_n;
rsbac_list_ta_number_t ta_number = 0;

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [-v] [-e] module target-type attribute [request] value id(s)\n"), progname);
      printf(gettext(" -v = verbose, -m = remove all attributes\n"));
      printf(gettext(" -r = recurse into subdirs, -n = list all requests\n"));
      printf(gettext(" -a = list attributes and values\n"));
      printf(gettext(" -V version = supply RSBAC integer version number for upgrading\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 or RC\n"));
      printf(gettext(" target-type = NETDEV, NETTEMP or NETOBJ\n"));
      printf(gettext(" category = category number for mac_categories\n"));
      printf(gettext(" request = request number for log_array_low|high\n"));
    }

int process(u_int request, char * tvalue, char * name)
  {
    int res = 0;
    char tmp1[120];
    union rsbac_target_id_t tid;

    switch(target)
      {
        case T_NETDEV:
          strncpy((char *)tid.netdev, name, RSBAC_IFNAMSIZ);
          tid.netdev[RSBAC_IFNAMSIZ] = 0;
          break;
        case T_NETTEMP:
          tid.nettemp = strtoul(name, NULL, 10);
          break;
        case T_NETOBJ:
          break;
        default:
          fprintf(stderr, gettext("Internal error on %s %s!\n"),
                  target_n, name);
          return(1);
      }
    if(doremove)
      {
        if(verbose)
          printf("Removing attributes for %s %s\n",
                 target_n, name);
        if(target == T_NETOBJ)
          res = rsbac_remove_target_n(ta_number, target, name);
        else
          res = rsbac_remove_target(ta_number, target, &tid);
        show_error(res);
        return res;
      }
      switch (attr)
        {
          case A_mac_categories:
          case A_local_mac_categories:
          case A_remote_mac_categories:
            if(request <= RSBAC_MAC_MAX_CAT)
              {
                u_int tmpval;

                if(target == T_NETOBJ)
                  res = rsbac_get_attr_n(ta_number, module, target, name, attr, &value, FALSE);
                else
                  res = rsbac_get_attr(ta_number, module, target, &tid, attr, &value, FALSE);
                show_error(res);
                if(res)
                  return res;
                tmpval = strtoul(tvalue,0,0);
                if(tmpval)
                  value.mac_categories |= RSBAC_MAC_CAT_VECTOR(request);
                else
                  value.mac_categories &= ~RSBAC_MAC_CAT_VECTOR(request);
              }
            else
              {
                if(strlen(tvalue) == RSBAC_MAC_NR_CATS)
                  {
                    strtou64mac(tvalue, &value.mac_categories);
                  }
                else
                  {
                    fprintf(stderr, gettext("Wrong argument length for attr mac_categories\n"));
                    exit(1);
                  }
              }
            break;
          case A_log_array_low:
          case A_log_array_high:
            if(   (target != T_NETDEV)
               && (target != T_NETTEMP)
              )
              {
                error_exit(-RSBAC_EINVALIDTARGET);
              }
            if(strlen(tvalue) == R_NONE)
              {
                strtou64log(tvalue, &value.log_array_low);
              }
            else
            if(request != R_NONE)
              {
                u_int catval;
                union rsbac_attribute_value_t value2;
                rsbac_log_array_t k;

                if(request > RSBAC_MAC_MAX_CAT)
                  {
                    fprintf(stderr, gettext("Invalid request number %u\n"), request);
                    exit(1);
                  }
                catval = strtol(tvalue,0,10);
                if(catval > 3)
                  {
                    fprintf(stderr, gettext("Invalid value %s\n"), tvalue);
                    exit(1);
                  }
                if(target == T_NETOBJ) {
                  res = rsbac_get_attr_n(ta_number, module, target, name, A_log_array_low, &value, FALSE);
                  error_exit(res);
                  res = rsbac_get_attr_n(ta_number, module, target, name, A_log_array_high, &value2, FALSE);
                  error_exit(res);
                } else {
                  res = rsbac_get_attr(ta_number, module, target, &tid, A_log_array_low, &value, FALSE);
                  error_exit(res);
                  res = rsbac_get_attr(ta_number, module, target, &tid, A_log_array_high, &value2, FALSE);
                  error_exit(res);
                }
                k = ((__u64) 1) << request;
                if(catval & 1)
                  value.log_array_low |= k;
                else
                  value.log_array_low &= ~k;
                if(catval & 2)
                  value2.log_array_high |= k;
                else
                  value2.log_array_high &= ~k;
                if(target == T_NETOBJ) {
                  res = rsbac_set_attr_n(ta_number, module, target, name, A_log_array_low, &value);
                  error_exit(res);
                  res = rsbac_set_attr_n(ta_number, module, target, name, A_log_array_high, &value2);
                  error_exit(res);
                } else {
                  res = rsbac_set_attr(ta_number, module, target, &tid, A_log_array_low, &value);
                  error_exit(res);
                  res = rsbac_set_attr(ta_number, module, target, &tid, A_log_array_high, &value2);
                  error_exit(res);
                }
                exit(0);
              }
            else
              {
                fprintf(stderr, gettext("Wrong number of arguments for attr %u\n"), attr);
                exit(1);
              }
            break;
          case A_security_level:
          case A_local_sec_level:
          case A_remote_sec_level:
            value.u_char_dummy = strtoul(tvalue, NULL, 10);
            break;
          case A_rc_type:
          case A_rc_type_nt:
          case A_local_rc_type:
          case A_remote_rc_type:
            value.rc_type = strtoul(tvalue, NULL, 10);
            break;
          default:
            value.dummy = strtol(tvalue, NULL, 0);
        }
    if(verbose)
      printf(gettext("Processing %s '%s', attribute %s\n"),
             target_n,
             name,
             get_attribute_name(tmp1, attr));
    if(target == T_NETOBJ)
      res = rsbac_set_attr_n(ta_number, module, target, name, attr, &value);
    else
      res = rsbac_set_attr(ta_number, module, target, &tid, attr, &value);
    if(res)
      {
        get_error_name(tmp1,res);
        fprintf(stderr, gettext("error: %s\n"), tmp1);
      }
    return(res);
  }


int main(int argc, char ** argv)
{
  enum rsbac_attribute_t attr_list_dev[RSBAC_NETDEV_NR_ATTRIBUTES] = RSBAC_NETDEV_ATTR_LIST;
  enum rsbac_attribute_t attr_list_temp[RSBAC_NETTEMP_NR_ATTRIBUTES] = RSBAC_NETTEMP_ATTR_LIST;
  enum rsbac_attribute_t attr_list_obj[RSBAC_NETOBJ_NR_ATTRIBUTES] = RSBAC_NETOBJ_ATTR_LIST;
  int res = 0;
  char tmp1[RSBAC_MAXNAMELEN],tmp2[RSBAC_MAXNAMELEN],tmp3[RSBAC_MAXNAMELEN];
  int i,j;

  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 'r':
                recurse=1;
                break;
              case 'm':
                doremove=1;
                break;
              case 'n':
                for(i=0; i<R_NONE; i++)
                  printf("%s\n", get_request_name(tmp1, 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 lists:\n"));
                printf("NETDEV:\n");
                for (j=0;j<RSBAC_NETDEV_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);
                  }
                printf("\nNETTEMP:\n");
                for (j=0;j<RSBAC_NETTEMP_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(attr_list_temp[j]));
                    get_attribute_name(tmp2,attr_list_temp[j]);
                    get_attribute_param(tmp3,attr_list_temp[j]);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                  }
                printf("\nNETOBJ:\n");
                for (j=0;j<RSBAC_NETOBJ_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(attr_list_obj[j]));
                    get_attribute_name(tmp2,attr_list_obj[j]);
                    get_attribute_param(tmp3,attr_list_obj[j]);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                  }
                exit(0);
              case 'V':
                if(argc < 3)
                  {
                    fprintf(stderr, gettext("%s: no version number for switch V\n"), progname);
                    exit(1);
                  }
                version = strtol(argv[2],0,10);
                argv++;
                argc--;
                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)
    {
      module = get_switch_target_nr(argv[1]);
      if(module != SW_NONE)
        {
          argv++;
          argc--;
        }
    }
  if (   (argc > 4)
      || (doremove && (argc > 2))
     )
    {
      u_int request = R_NONE;

      target = get_target_nr(argv[1]);
      switch(target)
        {
          case T_NETDEV:
          case T_NETTEMP:
          case T_NETOBJ:
            break;
          default:
            fprintf(stderr, gettext("%s: invalid target %s\n"), progname, argv[1]);
            exit(1);
        }
      target_n = argv[1];
      if(!doremove)
        {
          attr = get_attribute_nr(argv[2]);
          attr_n = argv[2];
          switch(attr)
            {
              case A_log_array_low:
              case A_local_log_array_low:
              case A_remote_log_array_low:
              case A_log_array_high:
              case A_local_log_array_high:
              case A_remote_log_array_high:
                request = get_request_nr(argv[3]);
                if(request != R_NONE)
                  {
                    argv++;
                    argc--;
                  }
                break;
              case A_mac_categories:
              case A_local_mac_categories:
              case A_remote_mac_categories:
                if(   !strcmp(argv[3], "CAT")
                   && (argc > 4)
                  )
                  {
                    request = strtoul(argv[4],0,10);
                    if(   (request > 0)
                       || !strcmp(argv[4],"0")
                      )
                      {
                        argv+=2;
                        argc-=2;
                      }
                    else
                      {
                        fprintf(stderr, "Invalid category after CAT parameter!\n");
                        exit(1);
                      }
                  }
                else
                  request = RSBAC_MAC_MAX_CAT + 1;
                break;
              default:
                break;
            }
        }
      if(!doremove)
        {
          if(verbose)
            printf(gettext("%s: %i targets\n\n"), progname, argc - 4);
          for (i=4;i < (argc);i++)
            {
              if(process(request, argv[3], argv[i]))
                res++;
            }
        }
      else
        {
          if(verbose)
            printf(gettext("%s: %i targets\n\n"), progname, argc - 2);
          for (i=2;i < (argc);i++)
            {
              if(process(request, NULL, argv[i]))
                res++;
            }
        }
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
