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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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[] = "auth_set_cap";
int recurse = 0;
int verbose = 0;
/* default max number of cap entries per file */
#define MAXNUM 200
int maxnum = MAXNUM;
char * filename = NULL;
char * filelistname = NULL;
struct rsbac_auth_cap_range_t * caplist;
rsbac_time_t * ttllist;
rsbac_list_ta_number_t ta_number = 0;
char * progname;

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] 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(" -m = set maximum length of cap entry list per file, default is %u\n"), MAXNUM);
      printf(gettext(" -o target-file = write to file, not stdout\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];
    int i;
    struct stat buf;

    if(verbose)
      printf(gettext("Processing FILE/DIR '%s'\n"), name);
    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_real, caplist, ttllist, maxnum);
    if(res<0)
      {
        if(   verbose
           || (errno != RSBAC_EINVALIDTARGET)
          ) {
          get_error_name(tmp1,res);
          fprintf(stderr, "%s: %s\n",
                  name,
                  tmp1);
        }
      }
    else
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i real caps\n", name, res);
        for(i=0;i<res;i++)
          if(ttllist[i])
            {
              if(caplist[i].first == caplist[i].last)
                fprintf(tfile,
                        "%s -T %u FD add \"%s\" %u/%u\n",
                        set_prog,
                        now + ttllist[i],
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first));
              else
                fprintf(tfile,
                        "%s -T %u FD add \"%s\" %u/%u %u/%u\n",
                        set_prog,
                        now + ttllist[i],
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first),
                        RSBAC_UID_SET(caplist[i].last),
                        RSBAC_UID_NUM(caplist[i].last));
            }
          else
            {
              if(caplist[i].first == caplist[i].last)
                fprintf(tfile,
                        "%s FD add \"%s\" %u/%u\n",
                        set_prog,
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first));
              else
                fprintf(tfile,
                        "%s FD add \"%s\" %u/%u %u/%u\n",
                        set_prog,
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first),
                        RSBAC_UID_SET(caplist[i].last),
                        RSBAC_UID_NUM(caplist[i].last));
            }
      }
    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_eff, caplist, ttllist, maxnum);
    if(res>0)
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i eff caps\n", name, res);
        for(i=0;i<res;i++)
          {
            if(ttllist[i])
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -e -T %u FD add \"%s\" %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -e -T %u FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
            else
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -e FD add \"%s\" %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -e FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
          }
      }
    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_fs, caplist, ttllist, maxnum);
    if(res>0)
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i fs caps\n", name, res);

        for(i=0;i<res;i++)
          {
            if(ttllist[i])
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -f -T %u FD add \"%s\" %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -f -T %u FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
            else
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -f FD add \"%s\" %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -f FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
          }
      }

    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_group_real, caplist, ttllist, maxnum);
    if(res<0)
      {
        if(   verbose
           || (errno != RSBAC_EINVALIDTARGET)
          ) {
          get_error_name(tmp1,res);
          fprintf(stderr, "%s: %s\n",
                  name,
                  tmp1);
        }
      }
    else
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i group real caps\n", name, res);
        for(i=0;i<res;i++)
          if(ttllist[i])
            {
              if(caplist[i].first == caplist[i].last)
                fprintf(tfile,
                        "%s -g -T %u FD add \"%s\" %u/%u\n",
                        set_prog,
                        now + ttllist[i],
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first));
              else
                fprintf(tfile,
                        "%s -g -T %u FD add \"%s\" %u/%u %u/%u\n",
                        set_prog,
                        now + ttllist[i],
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first),
                        RSBAC_UID_SET(caplist[i].last),
                        RSBAC_UID_NUM(caplist[i].last));
            }
          else
            {
              if(caplist[i].first == caplist[i].last)
                fprintf(tfile,
                        "%s -g FD add \"%s\" %u/%u\n",
                        set_prog,
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first));
              else
                fprintf(tfile,
                        "%s -g FD add \"%s\" %u/%u %u/%u\n",
                        set_prog,
                        name,
                        RSBAC_UID_SET(caplist[i].first),
                        RSBAC_UID_NUM(caplist[i].first),
                        RSBAC_UID_SET(caplist[i].last),
                        RSBAC_UID_NUM(caplist[i].last));
            }
      }
    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_group_eff, caplist, ttllist, maxnum);
    if(res>0)
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i group eff caps\n", name, res);
        for(i=0;i<res;i++)
          {
            if(ttllist[i])
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -E -T %u FD add \"%s\" %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -E -T %u FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
            else
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -E FD add \"%s\" %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -E FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
          }
      }
    res = rsbac_auth_get_f_caplist(ta_number, name, ACT_group_fs, caplist, ttllist, maxnum);
    if(res>0)
      {
        rsbac_time_t now = time(NULL);

        if(verbose)
          printf("# %s: %i group fs caps\n", name, res);
        for(i=0;i<res;i++)
          {
            if(ttllist[i])
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -F -T %u FD add \"%s\" %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -F -T %u FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          now + ttllist[i],
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
            else
              {
                if(caplist[i].first == caplist[i].last)
                  fprintf(tfile,
                          "%s -F FD add \"%s\" %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first));
                else
                  fprintf(tfile,
                          "%s -F FD add \"%s\" %u/%u %u/%u\n",
                          set_prog,
                          name,
                          RSBAC_UID_SET(caplist[i].first),
                          RSBAC_UID_NUM(caplist[i].first),
                          RSBAC_UID_SET(caplist[i].last),
                          RSBAC_UID_NUM(caplist[i].last));
              }
          }
      }

    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, tfile);
              }
          }
        closedir(dir_stream_p);
      }
    return(0);
  }

int main(int argc, char ** argv)
{
  int res = 0;
  int i;
  FILE * tfile;
  FILE * listfile = NULL;
  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 'm':
                if(argc > 2)
                  {
                    maxnum = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing maxnum value for parameter %c\n"), progname, *pos);
                break;
              case 'r':
                recurse=1;
                break;
              case 'o':
                if(argc > 2)
                  {
                    filename = argv[2];
                    argv++;
                    argc--;
                  }
                else
                  fprintf(stderr, gettext("%s: missing filename for parameter %c\n"), progname, *pos);
                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 '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));
          }
      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");
        }
      caplist = malloc(sizeof(*caplist) * maxnum);
      ttllist = malloc(sizeof(*ttllist) * maxnum);
      if(!caplist || !ttllist)
        error_exit(-ENOMEM);
      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);
}
