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

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

#define ROOM 10

enum rsbac_attribute_t attr_list[RSBAC_DEV_NR_ATTRIBUTES] = RSBAC_DEV_ATTR_LIST;
int recurse = 0;
int verbose = 0;
int backall = 0;
FILE * tfile;
char * filename = NULL;
char * filelistname = NULL;
rsbac_list_ta_number_t ta_number = 0;
char * progname;

__s64 def_attr[RSBAC_DEV_NR_ATTRIBUTES] = {
      SL_unclassified, /* sec_level */
      RSBAC_MAC_DEF_CAT_VECTOR, /* mac_categories */
      FALSE, /* mac_check */
      RSBAC_RC_GENERAL_TYPE /* rc_type */
  };

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [-v] [-o target-file] file/dirname(s)\n"), progname);  
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext(" -r = recurse in subdirs, -v = verbose, no symlinks followed,\n"));
      printf(gettext(" -T file = read file/dirname list from file (- for stdin),\n"));
      printf(gettext(" -o target-file = write to file, not stdout,\n"));
      printf(gettext(" -b = backup all device entries known to RSBAC,\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"));
    }

int process(char * name, struct rsbac_dev_desc_t * desc_p)
  {
    int res = 0;
    char tmpname[RSBAC_MAXNAMELEN];
    char tmp1[RSBAC_MAXNAMELEN];
    char tmp2[RSBAC_MAXNAMELEN];
    int j;
    union rsbac_attribute_value_t value;
    struct stat buf;
    union rsbac_target_id_t tid;

    if(verbose && name)
      printf(gettext("# Processing DEV '%s'\n"), name);
    for (j=0;j < RSBAC_DEV_NR_ATTRIBUTES;j++)
      {
        value.dummy = -1;
        if(backall)
          {
            tid.dev = *desc_p;
            res = rsbac_get_attr(ta_number, get_attr_module(attr_list[j]), T_DEV, &tid, attr_list[j], &value, 0);
          }
        else
          res = rsbac_get_attr_n(ta_number, get_attr_module(attr_list[j]), T_DEV, name, attr_list[j], &value, 0);
        if(res)
          {
            if(   (errno != RSBAC_EINVALIDMODULE)
               && (   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 != -1) {
                  if(backall)
                    fprintf(tfile,
                            "%s -V %u -d DEV \"%s\" %s %s\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            devdesctostr(tmpname,*desc_p),
                            get_attribute_name(tmp1,attr_list[j]),
                            u64tostrlog(tmp2,value.log_array_low));
                  else
                    fprintf(tfile,
                            "%s -V %u DEV \"%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_security_level:
                if(value.security_level != def_attr[j]) {
                  if(backall)
                    fprintf(tfile,
                            "%s -V %u -d DEV \"%s\" %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            devdesctostr(tmpname,*desc_p),
                            get_attribute_name(tmp1,attr_list[j]),
                            value.security_level);
                  else
                    fprintf(tfile,
                            "%s -V %u DEV \"%s\" %s %u\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            name,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.security_level);
                }
                break;
              case A_rc_type:
                if(backall)
                  {
                    if(   (   (desc_p->type > D_char)
                           && (value.rc_type != def_attr[j])
                          )
                       || (   (desc_p->type <= D_char)
                           && (value.rc_type != RC_type_inherit_parent)
                          )
                      )
                      fprintf(tfile,
                              "%s -V %u -d DEV \"%s\" %s %u\n",
                              set_prog,
                              RSBAC_VERSION_NR,
                              devdesctostr(tmpname,*desc_p),
                              get_attribute_name(tmp1,attr_list[j]),
                              value.rc_type);
                  }
                else
                  {
                    if(value.rc_type != RC_type_inherit_parent)
                      fprintf(tfile,
                              "%s -V %u DEV \"%s\" %s %u\n",
                              set_prog,
                              RSBAC_VERSION_NR,
                              name,
                              get_attribute_name(tmp1,attr_list[j]),
                              value.rc_type);
                  }
                break;
              default:
                if(value.dummy != def_attr[j]) {
                  if(backall)
                    fprintf(tfile,
                            "%s -V %u -d DEV \"%s\" %s %i\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            devdesctostr(tmpname,*desc_p),
                            get_attribute_name(tmp1,attr_list[j]),
                            value.dummy);
                  else
                    fprintf(tfile,
                            "%s -V %u DEV \"%s\" %s %i\n",
                            set_prog,
                            RSBAC_VERSION_NR,
                            name,
                            get_attribute_name(tmp1,attr_list[j]),
                            value.dummy);
                }
            }
      }
    if(   recurse
       && !backall
       && !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, NULL);
              }
          }
        closedir(dir_stream_p);
      }
    return(res);
  }

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

  locale_init();

  progname = argv[0];
  {
    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 'r':
                recurse=1;
                break;
              case 'b':
                backall=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 'a':
              case 'A':
                printf(gettext("Attributes and values in backup = see following list:\n"));
                for (j=0;j<RSBAC_DEV_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 '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)
      || backall
      || filelistname
     )
    {
      if(!filename)
        tfile = stdout;
      else
        {
          if (!(tfile=fopen(filename,"w")))
            {
              fprintf(stderr, gettext("opening target file returned error: %s\n"),
                              strerror(errno));
            }
        }
      if(backall)
        {
          int count;
          struct rsbac_dev_desc_t * id_array;

          count = rsbac_list_all_dev(ta_number, NULL, 0);
          error_exit(count);
          if(verbose)
            printf(gettext("# %s: %i targets\n"), progname, count);
          count += ROOM;
          id_array = malloc(count * sizeof(*id_array));
          if(!id_array)
            error_exit(-ENOMEM);
          count = rsbac_list_all_dev(ta_number, id_array, count);
          if(count > 0)
            {
              qsort(id_array, count, sizeof(*id_array), rsbac_dev_compare);
              for(i=0; i < count ; i++)
                process(NULL, &id_array[i]);
            }
        }
      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 - 1);
              if(filelistname)
                printf(gettext("# - plus targets from file %s\n"), filelistname);
            }
          for (i=1;i < argc;i++)
            {
              process(argv[i], NULL);
            }
          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)
                    process(item, NULL);
                }
              fclose(listfile);
            }
        }
      if(tfile != stdout)
        fclose(tfile);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}

