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

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

int verbose=0;
int recurse=0;
int printall=0;
int effective=1;
int scripting=0;
int exit_res=0;
int numdev=0;
int nonumeric=0;
rsbac_list_ta_number_t ta_number = 0;
union rsbac_attribute_value_t value;
enum rsbac_target_t target;
char * target_n;
enum rsbac_attribute_t attr;
char * progname;
enum rsbac_acl_subject_type_t subj_type=ACLS_USER;
rsbac_acl_subject_id_t subj_id;
char none_name[] = "FD";

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [switches] target-type file/dirname(s)\n"), progname);  
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext(" -v = verbose, -r = recurse into subdirs,\n"));
      printf(gettext(" -p = print right names, -d = give direct, not effective rights\n"));
      printf(gettext(" -n = list valid SCD names, -s = scripting mode\n"));
      printf(gettext(" -z = do not also try numeric username as uid\n"));
      printf(gettext(" -D = numeric device specification ({b|c}major[:minor])\n"));
      printf(gettext(" -R = list valid right names [for target-type]\n"));
      printf(gettext(" -N ta = transaction number (default = value of RSBAC_TA, if set, or 0)\n"));
      printf(gettext(" -u user = print rights for given user, not caller\n"));
      printf(gettext(" -g group = print rights for given group, not caller\n"));
      printf(gettext(" -l role = print rights for given role, not caller\n"));
      printf(gettext("  target-type = FILE, DIR, FIFO, SYMLINK, DEV, IPC, SCD, USER, GROUP, PROCESS,\n"));
      printf(gettext("                NETDEV, NETTEMP_NT, NETTEMP, NETOBJ or FD\n"));
      printf(gettext("  (FD: let %s decide between FILE, DIR, FIFO and SYMLINK, no DEV),\n"), progname);
      printf(gettext("  (IPC, PROCESS: only :DEFAULT:\n"));
      printf(gettext("  (NETTEMP: no :DEFAULT:\n"));
      printf(gettext("- Use name :DEFAULT: for default ACL\n"));
    }

int process(char * name)
  {
    int res = 0;
    char tmp1[RSBAC_MAXNAMELEN];
    struct stat buf;
    struct rsbac_acl_syscall_arg_t   arg;
    struct rsbac_acl_syscall_n_arg_t arg_n;
    rsbac_acl_rights_vector_t rights_vector = 0;

    if(!strcmp(name,":DEFAULT:"))
      {
        switch(target)
          {
            case T_FILE:
            case T_DIR:
            case T_FIFO:
            case T_SYMLINK:
            case T_FD:
              arg_n.name = NULL;
              break;
            case T_DEV:
              if(numdev)
                arg.tid.dev = RSBAC_ZERO_DEV_DESC;
              else
                arg_n.name = NULL;
              break;
            case T_IPC:
              arg.tid.ipc.type = I_none;
              break;
            case T_SCD:
              arg.tid.scd = AST_none;
              break;
            case T_USER:
              arg.tid.user = RSBAC_NO_USER;
              break;
            case T_PROCESS:
              arg.tid.process = 0;
              break;
            case T_GROUP:
              arg.tid.group = RSBAC_NO_GROUP;
              break;
            case T_NETDEV:
              arg.tid.netdev[0] = 0;
              break;
            case T_NETTEMP_NT:
              arg.tid.nettemp = 0;
              break;
            case T_NETOBJ:
              arg.tid.netobj.sock_p = NULL;
              arg.tid.netobj.local_addr = NULL;
              arg.tid.netobj.local_len = 0;
              arg.tid.netobj.remote_addr = NULL;
              arg.tid.netobj.remote_len = 0;
              break;

            default:
              fprintf(stderr, gettext("Invalid target %u for %s, skipped!\n"),
                      target, name);
              return(1);
          }
      }
    else
      {
        switch(target)
          {
            case T_FILE:
            case T_DIR:
            case T_FIFO:
            case T_SYMLINK:
            case T_FD:
              arg_n.name = name;
              break;
            case T_DEV:
              if(numdev)
                {
                  if(strtodevdesc(name, &arg.tid.dev))
                    {
                      fprintf(stderr, gettext("%s is no valid device specification, skipped\n"),
                              name);
                      return(1);
                    }
                }
              else
                arg_n.name = name;
              break;
            case T_SCD:
              arg.tid.scd = get_acl_scd_type_nr(name);
              if((arg.tid.scd == ST_none) || (arg.tid.scd == AST_none))
                {
                  fprintf(stderr, gettext("%s is no valid SCD name, skipped\n"),
                           name);
                  return(1);
                }
              break;
            case T_USER:
              if(rsbac_get_uid(ta_number, &arg.tid.user, name, nonumeric))
                {
                  fprintf(stderr, gettext("%s: Invalid User %s!\n"),
                          progname, name);
                  exit(1);
                }
              break;
            case T_GROUP:
              if(rsbac_get_gid(ta_number, &arg.tid.group, name))
                {
                  fprintf(stderr, gettext("%s: Invalid Group %s!\n"),
                          progname, name);
                  exit(1);
                }
              break;
            case T_NETDEV:
              strncpy((char *)arg.tid.netdev, name, RSBAC_IFNAMSIZ);
              arg.tid.netdev[RSBAC_IFNAMSIZ] = 0;
              break;
            case T_NETTEMP:
            case T_NETTEMP_NT:
              arg.tid.nettemp = strtoul(name, 0, 10);
              break;
            case T_NETOBJ:
              arg.tid.netobj.sock_p = (void *) strtoul(name, 0, 0);
              arg.tid.netobj.remote_addr = NULL;
              arg.tid.netobj.remote_len = 0;
              break;

            default:
              fprintf(stderr, gettext("Invalid target %u for %s, skipped!\n"),
                      target, name);
              return(1);
          }
      }
    if(verbose)
    printf(gettext("Processing %s '%s'\n"),
           target_n,
           name);
    switch(target)
      {
        case T_FILE:
        case T_DIR:
        case T_FIFO:
        case T_SYMLINK:
        case T_FD:
          arg_n.target = target;
          arg_n.subj_type = subj_type;
          arg_n.subj_id = subj_id;
          res = rsbac_acl_get_rights_n(ta_number, &arg_n, &rights_vector, effective);
          break;

        case T_DEV:
          if(!numdev)
            {
              arg_n.target = target;
              arg_n.subj_type = subj_type;
              arg_n.subj_id = subj_id;
              res = rsbac_acl_get_rights_n(ta_number, &arg_n, &rights_vector, effective);
              break;
            }
          /* fall through */

        default:
          arg.target = target;
          arg.subj_type = subj_type;
          arg.subj_id = subj_id;
          res = rsbac_acl_get_rights(ta_number, &arg, &rights_vector, effective);
      }
    if(res)
      {
        get_error_name(tmp1,res);
        if(   verbose
           || (errno != RSBAC_EINVALIDTARGET)
          )
          fprintf(stderr, gettext("%s: error: %s\n"), name, tmp1);
        exit_res=1;
      }
    else
      {
        if(scripting)
          {
            if(printall)
              {
                int i;

                for (i=0; i<R_NONE; i++)
                  if(rights_vector & ((rsbac_acl_rights_vector_t) 1 << i))
                    printf("%s\n", get_request_name(tmp1,i));
                for (i=RSBAC_ACL_SPECIAL_RIGHT_BASE; i<ACLR_NONE; i++)
                  if(rights_vector & ((rsbac_acl_rights_vector_t) 1 << i))
                    printf("%s\n", get_acl_special_right_name(tmp1,i));
              }
            else
              {
                printf("%s\n",
                       u64tostracl(tmp1, rights_vector));
              }
          }
        else
          {
            printf("%-19s: %s\n",
                   name, u64tostracl(tmp1, rights_vector));
            if(printall)
              {
                int i;

                for (i=0; i<R_NONE; i++)
                  if(rights_vector & ((rsbac_acl_rights_vector_t) 1 << i))
                    printf("  %s\n", get_request_name(tmp1,i));
                for (i=RSBAC_ACL_SPECIAL_RIGHT_BASE; i<ACLR_NONE; i++)
                  if(rights_vector & ((rsbac_acl_rights_vector_t) 1 << i))
                    printf("  %s\n", get_acl_special_right_name(tmp1,i));
              }
          }
      }

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

int main(int argc, char ** argv)
{
  int i;
  u_int stopflags = FALSE;

  locale_init();

  progname = argv[0];
  {
    char * env = getenv("RSBAC_TA");

    if(env)
      ta_number = strtoul(env,0,0);
  }
  subj_id=getuid();
  while((argc > 1) && (argv[1][0] == '-') && !stopflags)
    {
      char * pos = argv[1];
      pos++;
      while(*pos)
        {
          switch(*pos)
            {
              case 'h':
                use();
                return 0;
              case 'v':
                verbose=1;
                break;
              case 'r':
                recurse=1;
                break;
              case 'p':
                printall=1;
                break;
              case 's':
                scripting=1;
                break;
              case 'd':
                effective=0;
                break;
              case 'z':
                nonumeric=1;
                break;
              case 'D':
                numdev=1;
                break;
              case 'n':
                {
                  char tmp[80];

                  for(i=0; i<ST_none; i++)
                    printf("%s\n", get_scd_type_name(tmp, i));
                  for(i=AST_min; i<AST_none; i++)
                    printf("%s\n", get_acl_scd_type_name(tmp, i));
                  exit(0);
                }
              case 'R':
                {
                  char tmp[80];
                  rsbac_acl_rights_vector_t rights_vector = RSBAC_ALL_REQUEST_VECTOR;

                  if(argc > 2)
                    {
                      target = get_target_nr(argv[2]);
                      switch(target)
                        {
                          case T_FD:
                          case T_FILE:
                          case T_DIR:
                          case T_FIFO:
                          case T_SYMLINK:
                            rights_vector = RSBAC_FD_REQUEST_VECTOR;
                            break;
                          case T_DEV:
                            rights_vector = RSBAC_DEV_REQUEST_VECTOR;
                            break;
                          case T_IPC:
                            rights_vector = RSBAC_IPC_REQUEST_VECTOR;
                            break;
                          case T_SCD:
                            rights_vector = RSBAC_SCD_REQUEST_VECTOR;
                            if(argc > 3)
                              {
                                enum rsbac_acl_scd_type_t type;
                                
                                type = get_acl_scd_type_nr(argv[3]);
                                if(((enum rsbac_scd_type_t) type) == ST_other)
                                  rights_vector = RSBAC_NONE_REQUEST_VECTOR;
                              }
                            break;
                          case T_USER:
                            rights_vector = RSBAC_ACL_USER_RIGHTS_VECTOR;
                            break;
                          case T_GROUP:
                            rights_vector = RSBAC_ACL_GROUP_RIGHTS_VECTOR;
                            break;
                          case T_PROCESS:
                            rights_vector = RSBAC_PROCESS_REQUEST_VECTOR;
                            break;
                          case T_NETDEV:
                            rights_vector = RSBAC_NETDEV_REQUEST_VECTOR;
                            break;
                          case T_NETTEMP_NT:
                            rights_vector = RSBAC_NETTEMP_REQUEST_VECTOR;
                            break;
                          case T_NETTEMP:
                          case T_NETOBJ:
                            rights_vector = RSBAC_NETOBJ_REQUEST_VECTOR;
                            break;
                          default:
                            fprintf(stderr, gettext("%s: invalid target type %s for switch N\n"),
                                    progname, argv[2]);
                            exit(1);
                        }
                    }
                  for(i=0; i<R_NONE; i++)
                    if(rights_vector & RSBAC_ACL_RIGHTS_VECTOR(i))
                      printf("%s\n", get_request_name(tmp, i));
                  for(i=RSBAC_ACL_SPECIAL_RIGHT_BASE; i<ACLR_NONE; i++)
                    printf("%s\n", get_acl_special_right_name(tmp, i));
                  exit(0);
                }
              case 'u':
                if(argc < 3)
                  {
                    fprintf(stderr, gettext("%s: no user for switch u\n"), progname);
                    exit(1);
                  }
                {
                  rsbac_uid_t uid;

                  if(rsbac_get_uid(ta_number, &uid, argv[2], nonumeric))
                    {
                      fprintf(stderr, gettext("Invalid user %s!\n"), argv[2]);
                      exit(1);
                    }
                  subj_id = uid;
                }
                subj_type = ACLS_USER;
                if(!scripting) {
                  if (RSBAC_UID_SET(subj_id))
                    printf(gettext("%s: User %u/%u\n"),
                           progname,
                           RSBAC_UID_SET(subj_id),
                           RSBAC_UID_NUM(subj_id));
                  else
                    printf(gettext("%s: User %u\n"),
                           progname,
                           RSBAC_UID_NUM(subj_id));
                }
                argv++;
                argc--;
                break;
              case 'g':
                if(argc < 3)
                  {
                    fprintf(stderr, gettext("%s: no group for switch g\n"), progname);
                    exit(1);
                  }
                subj_type = ACLS_GROUP;
                subj_id = strtol(argv[2],0,10);
                if(!scripting)
                  printf(gettext("%s: Group %u\n"),
                         progname,
                         RSBAC_GID_NUM(subj_id));
                argv++;
                argc--;
                break;
              case 'l':
                if(argc < 3)
                  {
                    fprintf(stderr, gettext("%s: no role for switch l\n"), progname);
                    exit(1);
                  }
                subj_type = ACLS_ROLE;
                subj_id = strtol(argv[2],0,10);
                if(!scripting)
                  printf(gettext("%s: Role %u\n"), progname, RSBAC_UID_NUM(subj_id));
                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;
              case '-':
                if (!pos[1])
                  {
                    stopflags = TRUE;
                    break;
                  }
                if(!strcmp(pos,"-USER"))
                  {
                    if(argc < 3)
                      {
                        fprintf(stderr, gettext("%s: no user for switch -USER\n"), progname);
                        exit(1);
                      }
                    {
                      rsbac_uid_t uid;

                      if(rsbac_get_uid(ta_number, &uid, argv[2], nonumeric))
                        {
                          fprintf(stderr, gettext("Invalid user %s!\n"), argv[2]);
                          exit(1);
                        }
                      subj_id = uid;
                    }
                    subj_type = ACLS_USER;
                    if(!scripting) {
                      if (RSBAC_UID_SET(subj_id))
                        printf("%s: User %u/%u\n",
                               progname,
                               RSBAC_UID_SET(subj_id),
                               RSBAC_UID_NUM(subj_id));
                      else
                        printf("%s: User %u\n",
                               progname,
                               RSBAC_UID_NUM(subj_id));
                    }
                    argv++;
                    argc--;
                    pos+=4;
                    break;
                  }
                else
                if(!strcmp(pos,"-GROUP"))
                  {
                    if(argc < 3)
                      {
                        fprintf(stderr, gettext("%s: no group for switch -GROUP\n"), progname);
                        exit(1);
                      }
                    subj_type = ACLS_GROUP;
                    subj_id = strtol(argv[2],0,10);
                    if(!scripting)
                      printf(gettext("%s: Group %u\n"), progname, RSBAC_UID_NUM(subj_id));
                    argv++;
                    argc--;
                    pos+=5;
                    break;
                  }
                else
                if(!strcmp(pos,"-ROLE"))
                  {
                    if(argc < 3)
                      {
                        fprintf(stderr, gettext("%s: no role for switch -ROLE\n"), progname);
                        exit(1);
                      }
                    subj_type = ACLS_ROLE;
                    subj_id = strtol(argv[2],0,10);
                    if(!scripting)
                      printf(gettext("%s: Role %u\n"), progname, RSBAC_UID_NUM(subj_id));
                    argv++;
                    argc--;
                    pos+=4;
                    break;
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: unknown parameter %s\n"), progname, pos);
                  }
                break;

              default:
                fprintf(stderr, gettext("%s: unknown parameter %c\n"), progname, *pos);
                exit(1);
            }
          pos++;
        }
      argv++;
      argc--;
    }
  if (argc > 1)
    {
      target = get_target_nr(argv[1]);
      target_n = argv[1];
      if(target == T_NONE)
        {
          if(verbose)
            printf(gettext("%s: %i targets\n\n"), progname, argc - 1);
          fprintf(stderr, gettext("%s: No target type given, assuming FD\n"), progname);
          target = T_FD;
          target_n = none_name;
          if(argc < 1)
            process(".");
          else
            for (i=1;i < (argc);i++)
              {
                process(argv[i]);
              }
        }
      else
        {
          if(argc > 2)
            {
              if(verbose)
                printf(gettext("%s: %i targets\n\n"), progname, argc - 2);
              for (i=1;i < (argc-1);i++)
                {
                  process(argv[i+1]);
                }
            }
          else
            process(".");
        }
    }
  else
    {
      use();
      return 1;
    }
  return (exit_res);
}

