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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <rsbac/types.h>
#include <rsbac/syscalls.h>
#include <rsbac/getname.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>
#include "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define MAXNUM 200

char * progname;

void use(void)
  {
    printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
    printf(gettext("Use: %s [switches] TYPE add/remove target first_user [last_user]\n"), progname);
    printf(gettext("Use: %s [switches] TYPE get target\n"), progname);
    printf(gettext("     TYPE = PROCESS (add/remove only), DIR, FILE or FD (auto-select),\n"));
    printf(gettext("     target = pid or filename\n"));
    printf(gettext("     last_user: range from first_user to last_user\n"));
    printf(gettext(" -m = set maximum length of cap entry list per file, default is %u\n"), MAXNUM);
    printf(gettext(" -e = get or set caps for effective uids, not real\n"));
    printf(gettext(" -f = get or set caps for filesystem uids, not real\n"));
    printf(gettext(" -g = get or set caps for gids, not uids\n"));
    printf(gettext(" -E = get or set for eff gids, not real uids\n"));
    printf(gettext(" -F = get or set for fs gids, not real uids\n"));
    printf(gettext(" -t = set relative time-to-live for this cap entry in seconds (add only)\n"));
    printf(gettext(" -T = set absolute time-to-live for this cap entry in seconds (add only)\n"));
    printf(gettext(" -D = set relative time-to-live for this cap entry in days (add only)\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"));
  }

int main(int argc, char ** argv)
{
  int res = 0;
  rsbac_pid_t pid=0;
  struct rsbac_auth_cap_range_t cap_range;
  enum rsbac_target_t target;
  rsbac_boolean_t remove = FALSE;
  int verbose = 0;
  rsbac_version_t version=RSBAC_VERSION_NR;
  rsbac_time_t ttl = RSBAC_LIST_TTL_KEEP;
  int maxnum = MAXNUM;
  enum rsbac_auth_cap_type_t cap_type = ACT_real;
  struct rsbac_auth_cap_range_t * caplist;
  rsbac_time_t * ttllist;
  int i;
  rsbac_list_ta_number_t ta_number = 0;

  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 'e':
                cap_type = ACT_eff;
                break;
              case 'f':
                cap_type = ACT_fs;
                break;
              case 'g':
                cap_type = ACT_group_real;
                break;
              case 'E':
                cap_type = ACT_group_eff;
                break;
              case 'F':
                cap_type = ACT_group_fs;
                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 't':
                if(argc > 2)
                  {
                    ttl = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing ttl value for parameter %c\n"), progname, *pos);
                break;
              case 'D':
                if(argc > 2)
                  {
                    ttl = 86400 * strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing ttl value for parameter %c\n"), progname, *pos);
                break;
              case 'T':
                if(argc > 2)
                  {
                    rsbac_time_t now = time(NULL);
                    ttl = strtoul(argv[2], 0, 10);
                    if(ttl > now)
                      {
                        ttl -= now;
                        argc--;
                        argv++;
                      }
                    else
                      {
                        fprintf(stderr,
                                gettext("%s: ttl value for parameter %c is in the past, exiting\n"), progname, *pos);
                        exit(1);
                      }
                  }
                else
                  fprintf(stderr, gettext("%s: missing ttl value for parameter %c\n"), progname, *pos);
                break;
              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--;
    }
  switch(argc)
    {
      case 5:
      case 6:
        target = get_target_nr(argv[1]);
        if(   (target != T_PROCESS)
           && (target != T_FILE)
           && (target != T_DIR)
           && (target != T_FD)
          )
          {
            fprintf(stderr, gettext("%s: Invalid Target %s!\n"), progname, argv[1]);
            exit(1);
          }
        if(!strcmp(argv[2],"remove"))
          remove = TRUE;
        else
          if(strcmp(argv[2],"add"))
            {
              fprintf(stderr, gettext("%s: Invalid command %s!\n\n"), progname, argv[2]);
              exit(1);
            }
        if(target == T_PROCESS)
          pid=strtol(argv[3],0,10);
        if(rsbac_get_uid(ta_number, &cap_range.first, argv[4]))
          {
            fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                    progname, argv[4]);
            exit(1);
          }
        if(argc == 6)
          {
            if(rsbac_get_uid(ta_number, &cap_range.last, argv[5]))
              {
                fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                        progname, argv[5]);
                exit(1);
              }
            if(cap_range.last < cap_range.first)
              {
                fprintf(stderr, gettext("%s: Warning: first user %u/%u after last user %u/%u, exiting!\n\n"),
                        progname, RSBAC_UID_SET(cap_range.first), RSBAC_UID_NUM(cap_range.first), RSBAC_UID_SET(cap_range.last), RSBAC_UID_NUM(cap_range.last));
                exit(2);
              }
            if(RSBAC_UID_NUM(cap_range.last) > RSBAC_AUTH_MAX_RANGE_UID)
              {
                fprintf(stderr, gettext("%s: Warning: last user %u/%u uses special user ID, exiting!\n\n"),
                        progname, RSBAC_UID_SET(cap_range.last), RSBAC_UID_NUM(cap_range.last));
                exit(2);
              }
          }
        else
          { /* no last_user given */
            cap_range.last = cap_range.first;
          }
        if(target == T_PROCESS)
          {
            if(remove)
              res = rsbac_auth_remove_p_cap(ta_number, pid, cap_type, cap_range);
            else
              res = rsbac_auth_add_p_cap(ta_number, pid, cap_type, cap_range, ttl);
          }
        else
          {
            if(remove)
              res = rsbac_auth_remove_f_cap(ta_number, argv[3], cap_type, cap_range);
            else
              res = rsbac_auth_add_f_cap(ta_number, argv[3], cap_type, cap_range, ttl);
          }
        if(res < 0)
          error_exit(res);
        break;

      case 4:
        target = get_target_nr(argv[1]);
        if(   (target != T_FILE)
           && (target != T_DIR)
           && (target != T_FD)
           && (target != T_PROCESS)
          )
          {
            fprintf(stderr, gettext("%s: Invalid Target %s!\n"), progname, argv[1]);
            exit(1);
          }
        if(strcmp(argv[2],"get"))
          {
            fprintf(stderr, gettext("%s: Invalid command %s!\n\n"), progname, argv[2]);
            exit(1);
          }
        caplist = malloc(sizeof(*caplist) * maxnum);
        ttllist = malloc(sizeof(*ttllist) * maxnum);
        if(!caplist || !ttllist)
          error_exit(-ENOMEM);
        if(target == T_PROCESS)
          res = rsbac_auth_get_p_caplist(ta_number, strtoul(argv[3],0,0), cap_type, caplist, ttllist, maxnum);
        else
          res = rsbac_auth_get_f_caplist(ta_number, argv[3], cap_type, caplist, ttllist, maxnum);
        if(res < 0)
          {
            if(errno == RSBAC_ENOTFOUND)
              exit(0);
            else
              error_exit(res);
          }
        for(i=0; i<res; i++)
          if(ttllist[i])
            {
              if(caplist[i].first != caplist[i].last) {
                if(RSBAC_UID_SET(caplist[i].first) || RSBAC_UID_SET(caplist[i].last))
                  printf("%u/%u:%u/%u(ttl:%us) ", RSBAC_UID_SET(caplist[i].first), RSBAC_UID_NUM(caplist[i].first), RSBAC_UID_SET(caplist[i].last), RSBAC_UID_NUM(caplist[i].last), ttllist[i]);
                else
                  printf("%u:%u(ttl:%us) ", RSBAC_UID_NUM(caplist[i].first), RSBAC_UID_NUM(caplist[i].last), ttllist[i]);
              } else {
                if(RSBAC_UID_SET(caplist[i].first))
                  printf("%u/%u(ttl:%us) ", RSBAC_UID_SET(caplist[i].first), RSBAC_UID_NUM(caplist[i].first), ttllist[i]);
                else
                  printf("%u(ttl:%us) ", RSBAC_UID_NUM(caplist[i].first), ttllist[i]);
              }
            }
          else
            {
              if(caplist[i].first != caplist[i].last) {
                if(RSBAC_UID_SET(caplist[i].first) || RSBAC_UID_SET(caplist[i].last))
                  printf("%u/%u:%u/%u ", 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
                  printf("%u:%u ", RSBAC_UID_NUM(caplist[i].first), RSBAC_UID_NUM(caplist[i].last));
              } else {
                if(RSBAC_UID_SET(caplist[i].first))
                  printf("%u/%u ", RSBAC_UID_SET(caplist[i].first), RSBAC_UID_NUM(caplist[i].first));
                else
                  printf("%u ", RSBAC_UID_NUM(caplist[i].first));
              }
            }
        printf("\n");
        break;

      default:
        use();
        return 1;
    }
  exit(res);
}
