/*************************************************** */
/* 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 <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <termios.h>
#include <sys/mman.h>
#include <string.h>
#include <rsbac/types.h>
#include <rsbac/syscalls.h>
#include <rsbac/error.h>
#include "nls.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_SHADOW_H
#include <shadow.h>
#endif

#define MAX_TRIES 3
#define ROOM 20

  char * progname;
  char password[RSBAC_MAXNAMELEN] = "";
  char hash_algo[RSBAC_UM_ALGO_NAME_LEN] = "";
  char * int_pass = NULL;
  char * crypt_pass = NULL;
  rsbac_time_t ttl = 0;
  char * moregroups = NULL;
  char * skeldir = "/etc/skel/";
  int verbose = 0;
  int err;
  int useold = 0;
  int sysuser = 0;
  int addallold = 0;
  int homedirgiven = 0;
  int createhome = 0;
  int askpass = 0;
  int copy_pass = 0;
  int mayuidreplace = 0;
  int uidreplace = 0;
  int alwaysreplace = 0;
  int do_max_history = 0;
 __u8 max_history = 0;
  rsbac_list_ta_number_t ta_number = 0;
  rsbac_um_set_t vset = RSBAC_UM_VIRTUAL_KEEP;
  rsbac_gid_num_t * egroup_array = NULL;
  int egroup_num = 0;

void use(void)
    {
      printf(gettext("%s (RSBAC %s)\n***\n"), progname, VERSION);
      printf(gettext("Use: %s [flags] username\n"), progname);
      printf(gettext(" -h = this help, -- = no more flags,\n"));
      printf(gettext(" -c comment = fullname or comment,\n"));
      printf(gettext(" -d dir = homedir of user,\n"));
      printf(gettext(" -g group = main / initial Linux group,\n"));
      printf(gettext(" -G group1[,group2,...] = add more Linux groups,\n"));
      printf(gettext(" -p password = password in plaintext,\n"));
      printf(gettext(" -P = ask for password,\n"));
      printf(gettext(" -Q password = encrypted password (from backup),\n"));
      printf(gettext(" -A hash-algo = hash algorithm to use, e.g. sha256 (default: use kernel default),\n"));
      printf(gettext(" -s shell = user's shell,\n"));
      printf(gettext(" -u uid = uid to use,\n"));
      printf(gettext(" -i val = number of passwords to keep in history (0-255),\n"));
      printf(gettext(" -U = create system user (uid >= 100),\n"));
      printf(gettext(" -r = replace same username with different uid (requires -u),\n"));
      printf(gettext(" -R = replace same username in any case,\n"));
      printf(gettext(" -m = create user home dir from skeleton,\n"));
      printf(gettext(" -k dir = use this skeleton dir instead of /etc/skel/,\n"));
      printf(gettext(" -n minchange-days = minimum days between password changes,\n"));
      printf(gettext(" -x maxchange-days = maximum days between password changes,\n"));
      printf(gettext(" -w warnchange-days = warning days before password must be changed,\n"));
      printf(gettext(" -f inactive-days = period between password expiry and account disabling,\n"));
      printf(gettext(" -e expire-days = days since 1/Jan/1970 when account gets disabled,\n"));
      printf(gettext(" -t = set relative time-to-live in secs (role/type comp, admin, assign only)\n"));
      printf(gettext(" -T = set absolute time-to-live in secs (role/type comp, admin, assign only)\n"));
      printf(gettext(" -D = set relative time-to-live in days (role/type comp, admin, assign only)\n"));
      printf(gettext(" -o = use values from old passwd/shadow entry,\n"));
      printf(gettext(" -O = add all existing users (implies -o)\n"));
      printf(gettext(" -C user = copy existing user without password\n"));
      printf(gettext(" -K user = copy existing user with password\n"));
      printf(gettext(" -S n = virtual user set n\n"));
      printf(gettext(" -N ta = transaction number (default = value of RSBAC_TA, if set, or 0)\n"));
    }

int password_read(char * to, char * from)
  {
    char * f = from;
    char * t = to;
    char   tmp[3];
    int i;
    int res = 0;

    if(strlen(from) != RSBAC_UM_PASS_LEN * 2)
      {
        fprintf(stderr, gettext("Wrong encrypted password length!\n"));
        return -RSBAC_EINVALIDVALUE;
      }
    tmp[0] = 0;
    res = mlock(&tmp, 3);
    if (res) {
	    fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
    }
    
    tmp[2] = 0;
    while(f[0] && f[1])
      {
        tmp[0] = f[0];
        tmp[1] = f[1];
        i = strtoul(tmp, 0, 16);
        if(i < 0 || i > 255) {
		memset(&tmp, 0, 3);
		munlock(&tmp, 3);
		return -RSBAC_EINVALIDVALUE;
	}
        *t = i;
        t++;
        f += 2;
      }
    memset(&tmp, 0, 3);
    munlock(&tmp, 3);
    return 0;
  }

int get_pass(char * username, char ** my_int_pass_p)
  {
    char * pass1 = malloc(RSBAC_MAXNAMELEN);
    char * pass2 = malloc(RSBAC_MAXNAMELEN);
    struct termios old_term;
    struct termios tmp_term;
    int res;
    int i;

    if(!pass1)
      return -ENOMEM;
    if(!pass2)
      {
        free(pass1);
        return -ENOMEM;
      }
    pass1[0] = 0;
    res = mlock(pass1, RSBAC_MAXNAMELEN);
    if (res) {
	    fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
    }
    pass2[0] = 0;
    res = mlock(pass2, RSBAC_MAXNAMELEN);
    if (res) {
	    fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
    }
    for(i = 0; i < MAX_TRIES; i++)
      {
        printf("Password for user %s (empty password not allowed): ", username);
        if(isatty(STDIN_FILENO))
          {
            res = tcgetattr(STDIN_FILENO, &old_term);
            if(res)
              {
		memset(pass1, 0, RSBAC_MAXNAMELEN);
                munlock(pass1, RSBAC_MAXNAMELEN);
		free(pass1);
		memset(pass2, 0, RSBAC_MAXNAMELEN);
		munlock(pass2, RSBAC_MAXNAMELEN);
                free(pass2);
                return res;
              }
            memcpy(&tmp_term, &old_term, sizeof(old_term));
            tmp_term.c_lflag &= ~(ECHO);
            res = tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp_term);
            if(res)
              {
		memset(pass1, 0, RSBAC_MAXNAMELEN);
		munlock(pass1, RSBAC_MAXNAMELEN);
                free(pass1);
		memset(pass2, 0, RSBAC_MAXNAMELEN);
		munlock(pass2, RSBAC_MAXNAMELEN);
                free(pass2);
                return res;
              }
          }
        res = scanf("%254s", pass1);
        if(isatty(STDIN_FILENO))
          tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
        printf("\n");
        if(res < 0)
          {
            fprintf(stderr, gettext("%s: invalid password!\n"), progname);
	    memset(pass1, 0, RSBAC_MAXNAMELEN);
	    munlock(pass1, RSBAC_MAXNAMELEN);
	    free(pass1);
	    memset(pass2, 0, RSBAC_MAXNAMELEN);
	    munlock(pass2, RSBAC_MAXNAMELEN);
	    free(pass2);
            return -RSBAC_EINVALIDVALUE;
          }
        if(!res)
          {
            memset(pass1, 0, RSBAC_MAXNAMELEN);
	    munlock(pass1, RSBAC_MAXNAMELEN);
	    free(pass1);
	    memset(pass2, 0, RSBAC_MAXNAMELEN);
	    munlock(pass2, RSBAC_MAXNAMELEN);
            free(pass2);
            return 0;
          }
        printf("Repeat password for user %s: ", username);
        if(isatty(STDIN_FILENO))
          {
            res = tcgetattr(STDIN_FILENO, &old_term);
            if(res)
              {
		memset(pass1, 0, RSBAC_MAXNAMELEN);
		munlock(pass1, RSBAC_MAXNAMELEN);
                free(pass1);
		memset(pass2, 0, RSBAC_MAXNAMELEN);
		munlock(pass2, RSBAC_MAXNAMELEN);
                free(pass2);
                return res;
              }
            memcpy(&tmp_term, &old_term, sizeof(old_term));
            tmp_term.c_lflag &= ~(ECHO);
            res = tcsetattr(STDIN_FILENO, TCSAFLUSH, &tmp_term);
            if(res)
              {
                memset(pass1, 0, RSBAC_MAXNAMELEN);
		munlock(pass1, RSBAC_MAXNAMELEN);
		free(pass1);
                memset(pass2, 0, RSBAC_MAXNAMELEN);
		munlock(pass2, RSBAC_MAXNAMELEN);
		free(pass2);
                return res;
              }
          }
        res = scanf("%254s", pass2);
        if(isatty(STDIN_FILENO))
          tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
        printf("\n");
        if(res <= 0)
          {
            fprintf(stderr, gettext("%s: invalid password!\n"), progname);
            return -RSBAC_EINVALIDVALUE;
          }
        if(!strcmp(pass1,pass2))
          {
            *my_int_pass_p = pass1;
	    memset(pass2, 0, RSBAC_MAXNAMELEN);
	    munlock(pass2, RSBAC_MAXNAMELEN);
            free(pass2);
            return 0;
          }
        else
          fprintf(stderr, gettext("%s: password mismatch!\n"), progname);
      }
    fprintf(stderr, gettext("%s: Too many tries, using default password!\n"), progname);
    memset(pass1, 0, RSBAC_MAXNAMELEN);
    munlock(pass1, RSBAC_MAXNAMELEN);
    free(pass1);
    memset(pass2, 0, RSBAC_MAXNAMELEN);
    munlock(pass2, RSBAC_MAXNAMELEN);
    free(pass2);
    return 0;
  }

int process(char * name, rsbac_uid_t user, struct rsbac_um_user_entry_t entry)
    {
      int res = 0;
      char * my_int_pass = int_pass;

      if(useold)
        {
          struct passwd * pwentry;
          #ifdef HAVE_SHADOW_H
          struct spwd * spentry = NULL;
          #endif

          pwentry = getpwnam(name);
          if(!pwentry)
            {
              fprintf(stderr, "%s: old entry not found!\n", name);
              return -RSBAC_ENOTFOUND;
            }
          user = RSBAC_GEN_UID(vset, pwentry->pw_uid);
          entry.group = pwentry->pw_gid;
          strncpy(entry.fullname, pwentry->pw_gecos, RSBAC_UM_FULLNAME_LEN);
          entry.fullname[RSBAC_UM_FULLNAME_LEN - 1] = 0;
          strncpy(entry.homedir, pwentry->pw_dir, RSBAC_UM_HOMEDIR_LEN);
          entry.homedir[RSBAC_UM_HOMEDIR_LEN - 1] = 0;
          strncpy(entry.shell, pwentry->pw_shell, RSBAC_UM_SHELL_LEN);
          entry.shell[RSBAC_UM_SHELL_LEN - 1] = 0;
          entry.lastchange = 0;

          #ifdef HAVE_SHADOW_H
          spentry = getspnam(name);
          if(!spentry)
            {
              fprintf(stderr, "%s: old shadow entry not found, adding with default values!\n", name);
            }
          else
            {
              entry.minchange = spentry->sp_min;
              entry.maxchange = spentry->sp_max;
              entry.warnchange = spentry->sp_warn;
              entry.inactive = spentry->sp_inact;
              entry.expire = spentry->sp_expire;
              if(strlen(spentry->sp_pwdp) == 1)
                {
                  my_int_pass = NULL;
                  if(verbose)
                    printf("Account %s seems to be disabled, disabling password\n", name);
                }
              else
                if(askpass)
                  get_pass(name, &my_int_pass);
            }
          #else
          if(!strcmp(pwentry->pw_passwd, "*"))
            {
              if(askpass)
                get_pass(name, &my_int_pass);
              else
                fprintf(stderr, "%s: shadow not supported, adding with default values!\n", name);
            }
          else
            {
              if(strlen(pwentry->pw_passwd) == 1)
                {
                  my_int_pass = NULL;
                  if(verbose)
                    printf("Account %s seems to be disabled, disabling password\n", name);
                }
              else
                if(askpass)
                  get_pass(name, &my_int_pass);
            }
          #endif

          if(verbose) {
            if (RSBAC_UID_SET(user) == RSBAC_UM_VIRTUAL_KEEP)
              printf("Adding old user %u:%s\n", RSBAC_UID_NUM(user), name);
            else
              printf("Adding old user %u/%u:%s\n",
                     RSBAC_UID_SET(user), RSBAC_UID_NUM(user), name);
          }
        }
      else
        {
          if(sysuser) {
            if(RSBAC_UID_NUM(user) == RSBAC_NO_USER)
              user = RSBAC_GEN_UID(RSBAC_UID_SET(user), 100);
            while (rsbac_um_user_exists(ta_number, user))
              user++;
          }
          if(verbose) {
            if (RSBAC_UID_SET(user) == RSBAC_UM_VIRTUAL_KEEP)
              printf("Adding user %u:%s", RSBAC_UID_NUM(user), name);
            else
              printf("Adding user %u/%u:%s",
                     RSBAC_UID_SET(user), RSBAC_UID_NUM(user), name);
            if(alwaysreplace)
              printf(" (replace existing)");
            else
              if(uidreplace && mayuidreplace)
                printf(" (replace different uid)");
            printf("\n");
          }
          if(askpass)
            get_pass(name, &my_int_pass);
        }
      strncpy(entry.name, name, RSBAC_UM_NAME_LEN);
      entry.name[RSBAC_UM_NAME_LEN - 1] = 0;
      if(!homedirgiven && !useold)
        {
          snprintf(entry.homedir, RSBAC_UM_HOMEDIR_LEN, "/home/%s", name);
          entry.homedir[RSBAC_UM_HOMEDIR_LEN - 1] = 0;
        }
      if((uidreplace && mayuidreplace) || alwaysreplace)
        {
          rsbac_uid_t tmp_user = RSBAC_GEN_UID(vset, RSBAC_NO_USER);
          if(   !rsbac_um_get_uid(ta_number, name, &tmp_user)
             && (   alwaysreplace
                 || (   (RSBAC_UID_SET(user) == RSBAC_UM_VIRTUAL_KEEP)
                     && (RSBAC_UID_NUM(tmp_user) != RSBAC_UID_NUM(user))
                    )
                 || (   (RSBAC_UID_SET(user) != RSBAC_UM_VIRTUAL_KEEP)
                     && (tmp_user != user)
                    )
                )
            )
            {
              if(verbose)
                {
                  if (RSBAC_UID_SET(user) == RSBAC_UM_VIRTUAL_KEEP)
                    printf("First removing user %u:%s, then adding %u:%s\n",
                           RSBAC_UID_NUM(tmp_user), entry.name,
                           RSBAC_UID_NUM(user), entry.name);
                  else
                    printf("First removing user %u/%u:%s, then adding %u/%u:%s\n",
                           RSBAC_UID_SET(tmp_user), RSBAC_UID_NUM(tmp_user), entry.name,
                           RSBAC_UID_SET(tmp_user), RSBAC_UID_NUM(user), entry.name);
                }
              res = rsbac_um_remove_user(ta_number, tmp_user);
              if(res)
                {
                  if (vset != RSBAC_UM_VIRTUAL_KEEP)
                    fprintf(stderr, "%u/%s: ", RSBAC_UID_SET(user), name);
                  else
                    fprintf(stderr, "%s: ", name);
                  show_error(res);
                  return res;
                }
            }
        }
      if (hash_algo[0] == 0)
        res = rsbac_um_add_user(ta_number, user, &entry, my_int_pass, ttl);
      else
        res = rsbac_um_add_user_hash(ta_number, user, &entry, my_int_pass, hash_algo, ttl);
      if(my_int_pass && (my_int_pass != int_pass))
        free(my_int_pass);
      if(res)
        {
          fprintf(stderr, "%s: ", name);
          show_error(res);
          return res;
        }
      /* copy user home dir from skel */
      if(createhome)
        {
          struct stat statbuf;

          if(!stat(entry.homedir, &statbuf))
            {
              fprintf(stderr, "User %s homedir path %s already exists\n", name, entry.homedir);
            }
          else
            {
              char command[RSBAC_MAXNAMELEN];
              FILE * pfile;

              snprintf(command, RSBAC_MAXNAMELEN, "/bin/cp -a \"%s\" \"%s\"",
                       skeldir, entry.homedir);
              pfile = popen(command, "w");
              if(!pfile)
                {
                  fprintf(stderr, "Copying user %s homedir %s failed with error", name, entry.homedir);
                  show_error(res);
                  fprintf(stderr, "\n");
                }
              else
                {
                  pclose(pfile);
                  snprintf(command, RSBAC_MAXNAMELEN, "/bin/chown -R \"%s:\" \"%s\"",
                           name, entry.homedir);
                  pfile = popen(command, "w");
                  if(!pfile)
                    {
                      fprintf(stderr, "Chown of homedir %s to %s failed with error", entry.homedir, name);
                      show_error(res);
                      fprintf(stderr, "\n");
                    }
                  else
                    pclose(pfile);
                }
            }
        }

      if ((egroup_num > 0) && egroup_array)
        {
          int i;

          for (i=0; i<egroup_num; i++)
            {
              if(verbose)
                printf("Adding user %s group membership %u\n", name, egroup_array[i]);
              res = rsbac_um_add_gm(ta_number, user, egroup_array[i], 0);
              if(res)
                {
                  fprintf(stderr, "%s group membership %u: ", name, egroup_array[i]);
                  show_error(res);
                }
            }
        }
      if(moregroups)
        {
          char * p;
          char * m;
          rsbac_gid_t group;
          char * tmpgroups;

          tmpgroups = malloc(strlen(moregroups)+1);
          if (!tmpgroups)
            error_exit(-ENOMEM);
          strcpy(tmpgroups, moregroups);

          res = rsbac_um_get_uid(ta_number, entry.name, &user);
          error_exit(res);
          m = tmpgroups;
          p = tmpgroups;
          while(*m)
            {
              while(*p && (*p != ','))
                p++;
              group = RSBAC_GEN_GID(vset, RSBAC_NO_GROUP);
              if(*p)
                {
                  *p = 0;
                  if(rsbac_um_get_gid(ta_number, m, &group))
                    {
                      group = strtoul(m,0,0);
                      if(!group && strcmp(m,"0"))
                        {
                          fprintf(stderr, gettext("%s: Unknown group %s\n"), progname, m);
                          p++;
                          m = p;
                          continue;
                        }
                    }
                  printf("String %s, value %u\n", m, RSBAC_GID_NUM(group));
                  p++;
                  m = p;
                }
              else
                {
                  if(rsbac_um_get_gid(ta_number, m, &group))
                    {
                      group = strtoul(m,0,0);
                      if(!group && strcmp(m,"0"))
                        {
                          fprintf(stderr, gettext("%s: Unknown group %s\n"), progname, m);
                          m = p;
                          continue;
                        }
                    }
                  printf("String %s, value %u\n", m, RSBAC_GID_NUM(group));
                  m = p;
                }
              if(verbose)
                printf("Adding user %s group membership %u\n", name, RSBAC_GID_NUM(group));
              res = rsbac_um_add_gm(ta_number, user, group, 0);
              if(res)
                {
                  fprintf(stderr, "%s group membership %u: ", name, RSBAC_GID_NUM(group));
                  show_error(res);
                }
            }
          free(tmpgroups);
        }
      if(crypt_pass)
        {
          union rsbac_um_mod_data_t data;
	  int data_s;

          data_s = sizeof(&data);
	  res = mlock(&data, data_s);
	  if (res) {
		  fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
	  }
	  memcpy(data.string, crypt_pass, RSBAC_UM_PASS_LEN);
          res = rsbac_um_mod_user(ta_number, user, UM_cryptpass, &data);
	  memset(&data, 0, data_s);
	  munlock(&data, data_s);
          show_error(res);
        }
      if (do_max_history)
        {
          res = rsbac_um_set_max_history(ta_number, user, max_history);
          show_error(res);
        }
      return 0;
    }

int fill_entry(rsbac_uid_t user,
    struct rsbac_um_user_entry_t * entry_p)
{
  union rsbac_um_mod_data_t data;
  int res;

  res = rsbac_um_get_user_item(ta_number, user, UM_name, &data);
  if(!res)
    strcpy(entry_p->name, data.string);
  else
    return res;
  res = rsbac_um_get_user_item(ta_number, user, UM_fullname, &data);
  if(!res)
    strcpy(entry_p->fullname, data.string);
  res = rsbac_um_get_user_item(ta_number, user, UM_shell, &data);
  if(!res)
    strcpy(entry_p->shell, data.string);
  res = rsbac_um_get_user_item(ta_number, user, UM_homedir, &data);
  if(!res) {
    strcpy(entry_p->homedir, data.string);
    homedirgiven = TRUE;
  }
  if (copy_pass) {
    res = rsbac_um_get_user_item(ta_number, user, UM_pass, &data);
    if(!res) {
      crypt_pass = malloc(RSBAC_UM_PASS_LEN);
      if (crypt_pass)
        memcpy(crypt_pass, data.string, RSBAC_UM_PASS_LEN);
    }
  }
  res = rsbac_um_get_user_item(ta_number, user, UM_group, &data);
  if(!res)
    entry_p->group = data.group;

  egroup_num = rsbac_um_get_gm_list(ta_number, user, NULL, 0);
  if(egroup_num > 0)
    {
      egroup_num += ROOM;
      egroup_array = malloc(egroup_num * sizeof(*egroup_array));
      if(!egroup_array)
        return -RSBAC_ENOMEM;
      egroup_num = rsbac_um_get_gm_list(ta_number, user, egroup_array, egroup_num);
      if(egroup_num < 0) {
        egroup_num = 0;
        free(egroup_array);
        egroup_array = NULL;
        return -RSBAC_EREADFAILED;
      }
    }
              
  res = rsbac_um_get_user_item(ta_number, user, UM_lastchange, &data);
  if(!res)
    entry_p->lastchange = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_minchange, &data);
  if(!res)
    entry_p->minchange = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_maxchange, &data);
  if(!res)
    entry_p->maxchange = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_warnchange, &data);
  if(!res)
    entry_p->warnchange = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_inactive, &data);
  if(!res)
    entry_p->inactive = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_expire, &data);
  if(!res)
    entry_p->expire = data.days;
  res = rsbac_um_get_user_item(ta_number, user, UM_ttl, &data);
  if(!res)
    ttl = data.ttl;
  return 0;
}

int main(int argc, char ** argv)
{
  struct rsbac_um_user_entry_t entry = DEFAULT_UM_U_ENTRY;
  rsbac_uid_t user = RSBAC_GEN_UID(RSBAC_UM_VIRTUAL_KEEP, RSBAC_NO_USER);
  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 'o':
                useold = 1;
                break;
              case 'O':
                addallold = 1;
                useold = 1;
                break;
              case 'U':
                sysuser = 1;
                break;
              case 'K':
                copy_pass = 1;
                /* fall through */
              case 'C':
                if(argc > 2)
                  {
                    rsbac_uid_t euser = RSBAC_GEN_UID(vset, RSBAC_NO_USER);

                    if(rsbac_um_get_uid(ta_number, argv[2], &euser))
                      {
                        char * tmp_name = argv[2];
                        char * p = tmp_name;
                        rsbac_um_set_t tmp_vset = vset;

                        while (*p && (*p != '/'))
                          p++;
                        if (*p) {
                          *p = 0;
                          tmp_vset = strtoul(tmp_name, NULL, 0);
                          *p = '/';
                          p++;
                          tmp_name = p;
                        }
                        for (p = tmp_name; *p; p++)
                          {
                            if ((*p < '0') || (*p > '9'))
                              {
                                fprintf(stderr, gettext("%s: Unknown user %s\n"), progname, argv[2]);
                                return 1;
                              }
                          }
                        euser = strtoul(tmp_name, NULL, 0);
                        euser = RSBAC_GEN_UID(tmp_vset, euser);
                      }
                    if (fill_entry (euser, &entry)) {
                      fprintf(stderr, gettext("%s: Reading user %s (%u/%u) failed, exiting!\n"),
                              progname, argv[2], RSBAC_UID_SET(euser), RSBAC_UID_NUM(euser));
                      return 1;
                    }
                    user = euser;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'c':
                if(argc > 2)
                  {
                    strncpy(entry.fullname, argv[2], RSBAC_UM_FULLNAME_LEN);
                    entry.fullname[RSBAC_UM_FULLNAME_LEN - 1] = 0;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'd':
                if(argc > 2)
                  {
                    strncpy(entry.homedir, argv[2], RSBAC_UM_HOMEDIR_LEN);
                    entry.homedir[RSBAC_UM_HOMEDIR_LEN - 1] = 0;
                    homedirgiven = 1;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'p':
                if(argc > 2)
                  {
                    err = mlock(&password, RSBAC_MAXNAMELEN);
		    if (err) {
			    fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
		    }
		    strncpy(password, argv[2], RSBAC_MAXNAMELEN);
                    password[RSBAC_MAXNAMELEN - 1] = 0;
                    int_pass = password;
                    crypt_pass = NULL;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'A':
                if(argc > 2)
                  {
		    strncpy(hash_algo, argv[2], RSBAC_UM_ALGO_NAME_LEN);
                    hash_algo[RSBAC_UM_ALGO_NAME_LEN - 1] = 0;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'P':
                askpass = 1;
                break;
              case 'Q':
                if(argc > 2)
                  {
                    err = mlock(&password, RSBAC_MAXNAMELEN);
		    if (err) {
			    fprintf(stderr, gettext("Unable to lock password into physical memory!\n"));
		    }
		    err = password_read(password, argv[2]);
                    error_exit(err);
                    crypt_pass = password;
                    int_pass = NULL;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 's':
                if(argc > 2)
                  {
                    strncpy(entry.shell, argv[2], RSBAC_UM_SHELL_LEN);
                    entry.shell[RSBAC_UM_SHELL_LEN - 1] = 0;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'g':
                if(argc > 2)
                  {
                    rsbac_gid_t tmp_group = RSBAC_GEN_GID(RSBAC_UM_VIRTUAL_KEEP, RSBAC_NO_GROUP);

                    if(rsbac_get_gid_name(ta_number, &tmp_group, NULL, argv[2]))
                      {
                        fprintf(stderr, gettext("%s: Unknown group %s\n"), progname, argv[2]);
                        return 1;
                      }
                    entry.group = RSBAC_GID_NUM(tmp_group);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'G':
                if(argc > 2)
                  {
                    moregroups = argv[2];
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'u':
                if(argc > 2)
                  {
                    user = RSBAC_GEN_UID(vset, strtoul(argv[2],0,0));
                    mayuidreplace = 1;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'i':
                if(argc > 2)
                  {
                    max_history = strtoul(argv[2],0,0);
                    do_max_history = 1;
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'l':
                if(argc > 2)
                  {
                    entry.lastchange = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'n':
                if(argc > 2)
                  {
                    entry.minchange = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'x':
                if(argc > 2)
                  {
                    entry.maxchange = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'w':
                if(argc > 2)
                  {
                    entry.warnchange = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'f':
                if(argc > 2)
                  {
                    entry.inactive = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'e':
                if(argc > 2)
                  {
                    entry.expire = strtoul(argv[2],0,0);
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 'r':
                uidreplace = 1;
                break;
              case 'R':
                alwaysreplace = 1;
                break;
              case 'm':
                createhome = 1;
                break;
              case 'k':
                if(argc > 2)
                  {
                    struct stat statbuf;

                    if(stat(argv[2], &statbuf))
                      {
                        fprintf(stderr, gettext("%s: cannot lookup skel dir %s\n"), progname, argv[2]);
                        exit(1);
                      }
                    else
                      if(!S_ISDIR(statbuf.st_mode))
                        {
                          fprintf(stderr, gettext("%s: skel dir %s is no dir\n"), progname, argv[2]);
                          exit(1);
                        }
                    skeldir = argv[2];
                    if(strlen(skeldir) > RSBAC_MAXNAMELEN - 50)
                      {
                        fprintf(stderr, gettext("%s: skel dir name %s is too long\n"), progname, argv[2]);
                        exit(1);
                      }
                    argc--;
                    argv++;
                  }
                else
                  fprintf(stderr, gettext("%s: missing argument for parameter %c\n"), progname, *pos);
                break;
              case 't':
                if(argc > 2)
                  {
                    int res = strtokmgu32(argv[2], &ttl);
                    error_exit(res);
                    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 '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 'S':
                if(argc > 2)
                  {
                    if (rsbac_get_vset_num(argv[2], &vset))
                      {
                        fprintf(stderr, gettext("%s: invalid virtual set number for parameter %c\n"), progname, *pos);
                        exit(1);
                      }
                    user = RSBAC_GEN_UID(vset, user);
                    argc--;
                    argv++;
                  }
                else
                  {
                    fprintf(stderr, gettext("%s: missing virtual set number 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(addallold)
    {
      struct passwd * user_info_p;
      rsbac_uid_t tmp_uid; 

      setpwent();
      while((user_info_p = getpwent()))
        {
          if(   !rsbac_um_user_exists(ta_number, user_info_p->pw_uid)
             && rsbac_um_get_uid(ta_number, user_info_p->pw_name, &tmp_uid)
            )
            process(user_info_p->pw_name, user, entry);
          else
          if(verbose)
            printf("Skipping existing user %s / uid %u\n",
                  user_info_p->pw_name,
                  user_info_p->pw_uid);
        }
      endpwent();
      exit(0);
    }
  else
  if (argc > 1)
    {
      int i;

      for(i=1; i< argc; i++)
        {
          char * tmp_name = argv[i];
          char * p = tmp_name;
          rsbac_um_set_t tmp_vset = vset;

          while (*p && (*p != '/'))
            p++;
          if (*p) {
            *p = 0;
            if (rsbac_get_vset_num(tmp_name, &tmp_vset))
              {
                fprintf(stderr, gettext("%s: invalid virtual set number %s, skipping\n"), progname, tmp_name);
                continue;
              }
            *p = '/';
            p++;
            tmp_name = p;
          }
          process(tmp_name, RSBAC_GEN_UID(tmp_vset, user), entry);
        }
      memset(password, 0, RSBAC_MAXNAMELEN);
      munlock(&password, RSBAC_MAXNAMELEN);
      exit(0);
    }
  else
    {
      use();
      return 1;
    }
  exit(0);
}
