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

#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/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

const char   set_prog[] = "attr_set_file_dir";
enum rsbac_attribute_t attr_list[RSBAC_FD_NR_ATTRIBUTES] = RSBAC_FD_ATTR_LIST;
int recurse = 0;
int verbose = 0;
int printall = 0;
int exrdat = 0;
char * filename = NULL;
char * filelistname = NULL;
rsbac_pax_flags_t pax_flags = RSBAC_PAX_DEF_FLAGS;
rsbac_list_ta_number_t ta_number = 0;
char * progname;
rsbac_boolean_t module_enabled[SW_NONE+1];

__s64 def_attr[RSBAC_FD_NR_ATTRIBUTES] = {
      SL_inherit, /* sec_level */
      RSBAC_MAC_INHERIT_CAT_VECTOR, /* mac_categories */
      MA_inherit, /* mac_auto */
      FALSE, /* mac_prop_trusted */
      0, /* mac_file_flags */
      0, /* pm_object_class */
      0, /* pm_tp */
      PO_none, /* pm_object_type */
      FALSE, /* daz_scanner */
      RSBAC_FF_DEF, /* ff_flags */
      RC_type_inherit_parent, /* rc_type_fd */
      RC_default_force_role, /* rc_force_role */
      RC_default_initial_role, /* rc_initial_role */
      FALSE, /* auth_may_setuid */
      FALSE, /* auth_may_set_cap */
      FALSE, /* auth_learn */
      (rsbac_request_vector_t) -1,    /* log_array_low */
      (rsbac_request_vector_t) -1,    /* log_array_high */
      0,     /* log_program_based */
      0, /* symlink_add_remote_ip */
      FALSE, /* symlink_add_uid */
      FALSE, /* symlink_add_mac_level */
      FALSE, /* symlink_add_rc_role */
      LDD_inherit, /* linux_dac_disable */
      0,     /* min_caps */
      (__u32) -1, /* max_caps */
      LD_keep, /* cap_ld_env */
      0, /* res_min (not used) */
      0, /* res_max (not used) */
      RSBAC_PAX_DEF_FLAGS, /* pax_flags */
      FR_off, /* fake_root_uid */
      RSBAC_NO_USER, /* auid_exempt */
      DEFAULT_DAZ_FD_DO_SCAN, /* daz_do_scan */
      RSBAC_UM_VIRTUAL_KEEP /* vset */
  };

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [options] file/dirname(s)\n"), progname);  
      printf(gettext("- should be called by user with full attribute read access,\n  e.g. root with all modules off\n"));
      printf(gettext(" -r = recurse in subdirs, -v = verbose, no symlinks followed,\n"));
      printf(gettext(" -p = print requests, -s = ignore daz_scanned,\n"));
      printf(gettext(" -T file = read target list from file (- for stdin),\n"));
      printf(gettext(" -i = use MAC non-inherit values as default values,\n"));
      printf(gettext(" -P flags = use these PaX flags as default, preset is PeMRxS,\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(" -N ta = transaction number (default = value of RSBAC_TA, if set, or 0)\n"));
    }

int process(char * name, FILE * tfile)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN];
    char tmp2[RSBAC_MAXNAMELEN];
    int j,k;
    struct stat buf;
    union rsbac_attribute_value_t value;
    enum  rsbac_switch_target_t module;

    if(verbose)
      printf(gettext("# Processing FD '%s'\n"), name);
    if(   exrdat
       && !strcmp(name, "rsbac.dat")
      )
      return 0;
    for (j=0;j < RSBAC_FD_NR_ATTRIBUTES;j++)
      {
        module = get_attr_module(attr_list[j]);
        if (!module_enabled[module])
          continue;
        value.dummy = -1;
        res = rsbac_get_attr_n(ta_number, module, T_FD, name, attr_list[j], &value, 0);
        if(res)
          {
            if(errno == RSBAC_EINVALIDMODULE)
              module_enabled[module] = FALSE;
            else
              {
                if(   verbose
                   || (errno != RSBAC_EINVALIDTARGET)
                  )
                  {
                    get_error_name(tmp1,res);
                    fprintf(stderr, "%s (%s): %s\n",
                            name,
                            get_attribute_name(tmp2,attr_list[j]),
                            tmp1);
                  }
              }
          }
        else
          switch(attr_list[j])
            {
              case A_log_array_low:
              case A_log_array_high:
                if (value.log_array_low != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          u64tostrlog(tmp2,value.log_array_low));
                break;
              case A_log_program_based:
                if (value.log_program_based != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          u64tostrlog(tmp2,value.log_program_based));
                break;
              case A_mac_categories:
                if (value.mac_categories != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          u64tostrmac(tmp2,value.mac_categories));
                break;
              case A_ff_flags:
                if (value.ff_flags != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.ff_flags);
                break;
              case A_auid_exempt:
                if (value.auid_exempt != def_attr[j]) {
                  if (RSBAC_UID_SET(value.auid_exempt))
                    fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u/%u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          RSBAC_UID_SET(value.auid_exempt),
                          RSBAC_UID_NUM(value.auid_exempt));
                  else
                    fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          RSBAC_UID_NUM(value.auid_exempt));
                }
                break;
              case A_rc_type_fd:
              case A_rc_force_role:
              case A_rc_initial_role:
                if (value.rc_type_fd != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.rc_type_fd);
                break;
              case A_security_level:
              case A_pm_object_type:
                if (value.security_level != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.security_level);
                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 FD \"%s\" %s",
                            set_prog,
                            RSBAC_VERSION_NR,
                            name,
                            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
                      {
                        kcaptostrcap(tmp2, value.max_caps);
                        fprintf(tfile,
                            "%s -V %u FD \"%s\" %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            name,
                            get_attribute_name(tmp1,attr_list[j]),
                            tmp2);
                      }
                  }
                break;
              case A_mac_auto:
              case A_mac_prop_trusted:
              case A_mac_file_flags:
              case A_daz_do_scan:
              case A_daz_scanner:
              case A_auth_may_setuid:
              case A_linux_dac_disable:
              case A_fake_root_uid:
              case A_symlink_add_remote_ip:
              case A_cap_ld_env:
                if (value.u_char_dummy != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.u_char_dummy);
                break;
              case A_res_min:
              case A_res_max:
                for(k=0; k <= RSBAC_RES_MAX; k++)
                  {
                    if (value.res_array[k])
                      fprintf(tfile,
                              "%s -V %u FD \"%s\" %s %s %u\n",
                              set_prog,
                              RSBAC_VERSION_NR,
                              name,
                              get_attribute_name(tmp1,attr_list[j]),
                              get_res_name(tmp2,k),
                              value.res_array[k]);
                  }
                break;
              case A_pax_flags:
                if (value.pax_flags != pax_flags)
                  {
                    pax_print_flags(tmp2,value.pax_flags);
                    fprintf(tfile,
                            "%s -V %u FD \"%s\" %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            name,
                            get_attribute_name(tmp1,attr_list[j]),
                            tmp2);
                  }
                break;
              case A_vset:
                if (value.vset != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.vset);
                break;
              default:
                if(value.dummy != def_attr[j])
                  fprintf(tfile,
                          "%s -V %u FD \"%s\" %s %i\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          name,
                          get_attribute_name(tmp1,attr_list[j]),
                          value.dummy);
            }
      }
    if(   recurse
       && !lstat(name,&buf)
       && S_ISDIR(buf.st_mode))
      {
        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, tfile);
              }
          }
        closedir(dir_stream_p);
      }
    return 0;
  }

int main(int argc, char ** argv)
{
  int res = 0;
  char tmp1[RSBAC_MAXNAMELEN],tmp2[RSBAC_MAXNAMELEN],tmp3[RSBAC_MAXNAMELEN];
  int i,j;
  FILE * tfile;
  FILE * listfile = NULL;

  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] == '-'))
    {
      char * pos = argv[1];
      pos++;
      while(*pos)
        {
          switch(*pos)
            {
              case 'h':
                use();
                return 0;
              case 'v':
                verbose++;
                break;
              case 'p':
                printall=1;
                break;
              case 'r':
                recurse=1;
                break;
              case 'm':
                break;
              case 'x':
                exrdat=1;
                break;
              case 'i':
                def_attr[0] = SL_unclassified; /* sec_level */
                def_attr[2] = RSBAC_MAC_DEF_CAT_VECTOR; /* mac_categories */
                def_attr[3] = MA_inherit; /* mac_auto */
                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 T\n"), progname);
                break;
              case 'a':
                printf(gettext("attributes and values in backup = see following list:\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);
                  }
                exit(0);
              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 'P':
                if(argc > 2)
                  {
                    pax_flags = pax_strtoflags(argv[2], pax_flags);
                    if(verbose)
                      printf("# new PaX flags: %s\n", pax_print_flags(tmp2,pax_flags));
                    argc--;
                    argv++;
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: missing PaX flags 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 || filelistname)
    {
      if(!filename)
        tfile = stdout;
      else
        {
          if (!(tfile=fopen(filename,"w")))
            {
              fprintf(stderr, gettext("opening target file returned error: %s\n"),
                      strerror(errno));
              exit(1);
            }
        }
      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"), progname, argc - 1);
          if(recurse)
            printf(gettext(" - recursing"));
          if(filelistname)
            printf(gettext(" - plus targets from file %s"), filelistname);
          printf("\n");
        }
      for (i=1;i < (argc);i++)
        {
          process(argv[i],tfile);
        }
      if(filelistname)
        {
          char item[4096];
          char * pos;
          int last;

          pos = item;
          while(fgets(item, 4095, listfile))
            {
              if(!*item)
                continue;
              last = strlen(item) - 1;
              if(item[last] == '\n')
                item[last] = 0;
              if(*item)
                process(item, tfile);
            }
          fclose(listfile);
        }
      if(tfile != stdout)
        fclose(tfile);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
