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

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

int verbose=0;
int recurse=0;
rsbac_version_t version=RSBAC_VERSION_NR;
union rsbac_attribute_value_t value;
enum rsbac_switch_target_t module;
enum rsbac_target_t target;
enum rsbac_attribute_t attr;
char * progname;
char * target_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] [-r] module target-type attribute value file/dirname(s)\n"), progname);  
      printf(gettext(" -v = verbose, -r = recurse into subdirs,\n"));
      printf(gettext(" -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, FF, RC or AUTH\n"));
      printf(gettext(" target-type = FILE, DIR, FIFO, SYMLINK, DEV or FD\n"));
      printf(gettext(" (FD: let %s decide between FILE, DIR, FIFO and SYMLINK, no DEV),\n"), progname);
    }

int process(char * name)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN];
    char tmp2[RSBAC_MAXNAMELEN];
    struct stat buf;

    if(verbose)
      printf(gettext("Processing %s '%s', attribute %s, value %i\n"),
             target_n,
             name,
             get_attribute_name(tmp2,attr),
             value.dummy);
    res = rsbac_set_attr_n(ta_number, module, target, name, attr, &value);
    if(res)
      {
        if(   verbose
           || (errno != RSBAC_EINVALIDTARGET)
          ) {
          get_error_name(tmp1,res);
          fprintf(stderr, gettext("%s: error: %s\n"), name, tmp1);
        }
      }

    if(   !lstat(name,&buf)
       && S_ISDIR(buf.st_mode)
       && recurse)
      {
        DIR * dir_stream_p;
        struct dirent * dirent_p;
        char name2[PATH_MAX];

        if(S_ISLNK(buf.st_mode))
          return(0);
        if(!(dir_stream_p = opendir(name)))
          {
            fprintf(stderr, gettext("opendir for dir %s returned error: %s\n"),
                   name,
                   strerror(errno));
            return(-2);
          }
        while((dirent_p = readdir(dir_stream_p)))
          {
            if(   (strcmp(".",dirent_p->d_name))
               && (strcmp("..",dirent_p->d_name)) )
              {
                strcpy(name2,name);
                strcat(name2,"/");
                strcat(name2,dirent_p->d_name);
                process(name2);
              }
          }
        closedir(dir_stream_p);
      }
    return(0);
  }

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 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 'n':
                {
                  char tmp[80];
                  int i;

                  for(i=0; i<R_NONE; i++)
                    printf("%s\n", get_request_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 value (integer) = see following list:\n"));
                printf(gettext("- FILE, DIR, FIFO and SYMLINK:\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("- DEV:\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 '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)
    {
      if(verbose)
        printf("%s: %i targets\n\n", progname, argc - 4);
      target = get_target_nr(argv[1]);
      target_n = argv[1];
      if(   (target != T_DIR)
         && (target != T_FILE)
         && (target != T_FIFO)
         && (target != T_SYMLINK)
         && (target != T_DEV)
         && (target != T_FD)
        )
        {
          fprintf(stderr, gettext("%s: Invalid target type %s\n"), progname, argv[1]);
          exit(1);
        }
      attr = get_attribute_nr(argv[2]);
      switch(attr)
        {
          case A_none:
            fprintf(stderr, gettext("%s: Invalid attribute %s\n"), progname, argv[2]);
            exit(1);
          case A_res_min:
          case A_res_max:
            fprintf(stderr, gettext("%s: Attribute %s not supported\n"), progname, argv[2]);
            exit(1);
          case A_log_array_low:
          case A_log_array_high:
          case A_log_program_based:
            if(strlen(argv[3]) != R_NONE)
              {
                fprintf(stderr, gettext("%s: Invalid attribute value, length must be %i\n"),
                                progname, R_NONE);
                exit(1);
              }
            for(j=0;j<strlen(argv[3]);j++)
              {
                if(   argv[3][j] != '0'
                   && argv[3][j] != '1')
                  {
                    fprintf(stderr, gettext("%s: Invalid attribute value char, must be 0 or 1\n"), progname);
                    exit(1);
                  }
              }
            strtou64log(argv[4], &value.log_array_low);
            break;

          case A_security_level:
            value.security_level = strtoul(argv[3],0,10);
            break;

          case A_mac_categories:
            if(strlen(argv[3]) == RSBAC_MAC_NR_CATS)
              {
                for(j=0;j<strlen(argv[3]);j++)
                  {
                    if(   argv[3][j] != '0'
                       && argv[3][j] != '1')
                      {
                        fprintf(stderr, gettext("%s: Invalid attribute value char, must be 0 or 1\n"), progname);
                        exit(1);
                      }
                  }
                strtou64mac(argv[3], &value.mac_categories);
              }
            else
              {
                value.mac_categories = strtoul(argv[3], 0, 10);
              }
            break;

          case A_mac_auto:
            value.mac_auto = strtoul(argv[3],0,10);
            break;

          case A_mac_check:
            value.mac_check = strtoul(argv[3],0,10);
            break;

          case A_mac_prop_trusted:
            value.mac_prop_trusted = strtoul(argv[3],0,10);
            break;

          case A_mac_file_flags:
            value.mac_file_flags = strtoul(argv[3],0,10);
            break;

          case A_pm_object_class:
            value.pm_object_class = strtoul(argv[3],0,10);
            break;

          case A_pm_tp:
            value.pm_tp = strtoul(argv[3],0,10);
            break;

          case A_pm_object_type:
            value.pm_object_type = strtoul(argv[3],0,10);
            break;

          case A_daz_scanner:
            value.daz_scanner = strtoul(argv[3],0,10);
            break;

          case A_ff_flags:
            value.ff_flags = strtoul(argv[3],0,10);
            break;

          case A_rc_type:
          case A_rc_type_fd:
            value.rc_type = strtoul(argv[3],0,10);
            break;

          case A_rc_force_role:
          case A_rc_initial_role:
          case A_rc_role:
          case A_rc_def_role:
            value.rc_role = strtoul(argv[3],0,10);
            break;

          case A_auth_may_setuid:
            value.auth_may_setuid = strtoul(argv[3],0,10);
            break;

          case A_auth_may_set_cap:
            value.auth_may_set_cap = strtoul(argv[3],0,10);
            break;

          case A_auth_learn:
            value.auth_learn = strtoul(argv[3],0,10);
            break;

          case A_symlink_add_uid:
            value.symlink_add_uid = strtoul(argv[3],0,10);
            break;

          case A_symlink_add_mac_level:
            value.symlink_add_mac_level = strtoul(argv[3],0,10);
            break;

          case A_symlink_add_rc_role:
            value.symlink_add_rc_role = strtoul(argv[3],0,10);
            break;

          case A_linux_dac_disable:
            value.linux_dac_disable = strtoul(argv[3],0,10);
            break;

/*          case A_min_caps:
            value.min_caps = strtoul(argv[3],0,10);
            break;*/

/*          case A_max_caps:
            value.max_caps = strtoul(argv[3],0,10);
            break;*/

          case A_pax_flags:
            value.pax_flags = pax_strtoflags(argv[3], RSBAC_PAX_DEF_FLAGS);
            break;

          case A_fake_root_uid:
            value.fake_root_uid = strtoul(argv[3],0,10);
            break;

          case A_auid_exempt:
            value.auid_exempt = strtoul(argv[3],0,10);
            break;

          default:
            value.dummy = strtol(argv[3],0,10);
        }
      for (i=1;i < (argc-3);i++)
        {
          process(argv[i+3]);
        }
    }
  else
    {
      use();
      return 1;
    }
  return res;
}

