/*************************************************** */
/* 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/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_net";
enum rsbac_attribute_t netdev_attr_list[RSBAC_NETDEV_NR_ATTRIBUTES] = RSBAC_NETDEV_ATTR_LIST;
enum rsbac_attribute_t nettemp_attr_list[RSBAC_NETTEMP_NR_ATTRIBUTES] = RSBAC_NETTEMP_ATTR_LIST;
int verbose = 0;
int backall = 0;
rsbac_list_ta_number_t ta_number = 0;
char * filename = NULL;
enum rsbac_target_t target;
union rsbac_target_id_t tid;
char * progname;

#define LISTROOM 10

__s64 def_netdev_attr[RSBAC_NETDEV_NR_ATTRIBUTES] = {
      RSBAC_RC_GENERAL_TYPE, /* rc_type */
      -1,    /* log_array_low */
      -1    /* log_array_high */
  };

__s64 def_nettemp_attr[RSBAC_NETTEMP_NR_ATTRIBUTES] = {
      SL_unclassified, /* sec_level */
      RSBAC_MAC_DEF_CAT_VECTOR, /* mac_categories */
      RSBAC_PM_IPC_OBJECT_CLASS_ID, /* pm_object_class */
      0, /* pm_ipc_purpose */
      PO_ipc, /* pm_object_type */
      RSBAC_RC_GENERAL_TYPE, /* rc_type */
      RSBAC_RC_GENERAL_TYPE, /* rc_type_nt */
      -1,    /* log_array_low */
      -1     /* log_array_high */
  };

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [options] target name(s)/number(s)\n"), progname);  
      printf(gettext(" should be called by user with full attribute read access,\n- e.g. with all modules off\n"));
      printf(gettext(" -a = backup all objects, -v = verbose, no symlinks followed,\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(" valid targets: NETDEV, NETTEMP\n"));
    }

int process_netdev(char * name, FILE * tfile)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN];
    char tmp2[RSBAC_MAXNAMELEN];
    int j;
    union rsbac_attribute_value_t value;

    if(verbose)
      printf(gettext("# Processing NETDEV '%s'\n"), name);
    strncpy((char *)tid.netdev, name, RSBAC_IFNAMSIZ);
    tid.netdev[RSBAC_IFNAMSIZ] = 0;
    for (j=0;j < RSBAC_NETDEV_NR_ATTRIBUTES;j++)
      {
        value.dummy = -1;
        res = rsbac_get_attr(ta_number, get_attr_module(netdev_attr_list[j]),
                             target, &tid, netdev_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,netdev_attr_list[j]),
                        tmp1);
              }
          }
        else
          switch(netdev_attr_list[j])
            {
              case A_log_array_low:
              case A_log_array_high:
                if (value.log_array_low != def_netdev_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETDEV %s %s %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,netdev_attr_list[j]),
                          u64tostrlog(tmp2,value.log_array_low),
                          name);
                break;
              case A_rc_type:
                if (value.rc_type != def_netdev_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETDEV %s %u %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,netdev_attr_list[j]),
                          value.rc_type,
                          name);
                break;
              default:
                if(value.dummy != def_netdev_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETDEV %s %i %s\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,netdev_attr_list[j]),
                          value.dummy,
                          name);
            }
      }
    return(0);
  }

int process_nettemp(rsbac_net_temp_id_t id, FILE * tfile)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN];
    char tmp2[RSBAC_MAXNAMELEN];
    int j;
    union rsbac_attribute_value_t value;

    if(verbose)
      printf(gettext("# Processing NETTEMP %u\n"), id);
    tid.nettemp = id;
    for (j=0;j < RSBAC_NETTEMP_NR_ATTRIBUTES;j++)
      {
        value.dummy = -1;
        res = rsbac_get_attr(ta_number, get_attr_module(nettemp_attr_list[j]),
                             target, &tid, nettemp_attr_list[j], &value, 0);
        if(res)
          {
            if(   (errno != RSBAC_EINVALIDMODULE)
               && (   verbose
                   || (errno != RSBAC_EINVALIDTARGET)
                  )
              )
              {
                get_error_name(tmp1,res);
                fprintf(stderr, "%u (%s): %s\n",
                        id,
                        get_attribute_name(tmp2,nettemp_attr_list[j]),
                        tmp1);
              }
          }
        else
          switch(nettemp_attr_list[j])
            {
              case A_log_array_low:
              case A_log_array_high:
                if (value.log_array_low != def_nettemp_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETTEMP %s %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,nettemp_attr_list[j]),
                          u64tostrlog(tmp2,value.log_array_low),
                          id);
                break;
              case A_mac_categories:
                if (value.mac_categories != def_nettemp_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETTEMP %s %s %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,nettemp_attr_list[j]),
                          u64tostrmac(tmp2,value.mac_categories),
                          id);
                break;
              case A_rc_type:
                if (value.rc_type != def_nettemp_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETTEMP %s %u %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,nettemp_attr_list[j]),
                          value.rc_type,
                          id);
                break;
              case A_security_level:
              case A_pm_object_type:
                if (value.u_char_dummy != def_nettemp_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETTEMP %s %u %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,nettemp_attr_list[j]),
                          value.u_char_dummy,
                          id);
                break;
              default:
                if(value.u_dummy != def_nettemp_attr[j])
                  fprintf(tfile,
                          "%s -V %u NETTEMP %s %u %u\n",
                          set_prog,
                          RSBAC_VERSION_NR,
                          get_attribute_name(tmp1,nettemp_attr_list[j]),
                          value.u_dummy,
                          id);
            }
      }
    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;
  char * filelistname = NULL;

  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 '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':
                backall=1;
                break;
              case 'A':
                printf(gettext("attributes and values in backup = see following list:\n"));
                printf("NETDEV:\n");
                for (j=0;j<RSBAC_NETDEV_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(netdev_attr_list[j]));
                    get_attribute_name(tmp2,netdev_attr_list[j]);
                    get_attribute_param(tmp3,netdev_attr_list[j]);
                    printf("[%-4s] %s\n\t%s\n",tmp1,tmp2,tmp3);
                  }
                printf("NETTEMP:\n");
                for (j=0;j<RSBAC_NETTEMP_NR_ATTRIBUTES;j++)
                  {
                    get_switch_target_name(tmp1, get_attr_module(nettemp_attr_list[j]));
                    get_attribute_name(tmp2,nettemp_attr_list[j]);
                    get_attribute_param(tmp3,nettemp_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 (   ((backall || filelistname) && (argc > 1))
      || (argc > 2)
     )
    {
      if(!filename)
        tfile = stdout;
      else
        {
          if (!(tfile=fopen(filename,"w")))
            {
              fprintf(stderr, gettext("opening target file returned error: %s\n"),
                      strerror(errno));
            }
        }
      target = get_target_nr(argv[1]);
      switch(target)
        {
          case T_NETDEV:
          case T_NETTEMP:
            break;
          default:
            fprintf(stderr, gettext("invalid target %s\n"),
                    argv[1]);
        }
      if(target == T_NETDEV)
        {
          if(backall)
            {
              rsbac_netdev_id_t * netdev_array;
              long count;

              count = rsbac_net_list_all_netdev(ta_number, NULL, 0);
              error_exit(count);
              count += LISTROOM;
              netdev_array = malloc(count * sizeof(*netdev_array));
              if(!netdev_array)
                error_exit(-ENOMEM);
              count = rsbac_net_list_all_netdev(ta_number, netdev_array, count);
              for(i = 0; i< count ; i++)
                process_netdev((char *)netdev_array[i], tfile);
              free(netdev_array);
            }
          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 - 2);
                  if(filelistname)
                    printf(gettext("# - plus targets from file %s\n"), filelistname);
                }
              for (i=2;i < (argc);i++)
                process_netdev(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_netdev(item,tfile);
                    }
                  fclose(listfile);
                }
            }
        }
      else
        {
          if(backall)
            {
              rsbac_net_temp_id_t * temp_array;
              long count;

              count = rsbac_net_list_all_template(ta_number, NULL, 0);
              error_exit(count);
              count += LISTROOM;
              temp_array = malloc(count * sizeof(*temp_array));
              if(!temp_array)
                error_exit(-ENOMEM);
              count = rsbac_net_list_all_template(ta_number, temp_array, count);
              for(i = 0; i< count ; i++)
                process_nettemp(temp_array[i], tfile);
              free(temp_array);
            }
          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 - 2);
                  if(filelistname)
                    printf(gettext("# - plus targets from file %s\n"), filelistname);
                }
              for (i=2;i < (argc);i++)
                process_nettemp(strtoul(argv[i],0,0),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_nettemp(strtoul(item,0,0),tfile);
                    }
                  fclose(listfile);
                }
            }
        }
      if(tfile != stdout)
        fclose(tfile);
    }
  else
    {
      use();
      return 1;
    }
  return (res);
}
