Hi list,

Attached are the implementations for groupadd, useradd and mkpasswd
commands.

Patches are as follows

1. __lib.patch__  : this includes changes made to lib/lib.h and
__lib/password.c__.
lib/passowrd.c is modified to share the function __update_password()__
among groupadd, useradd commands.
This also has the factored out code, common to both, for __passwd__ and
__mkpasswd__.
Also has the cosmetic cleanup changes.

2. __passwd.c.patch__ : this file is modified due to the common code
factoring out and cosmetic cleanup changes.

3. groupadd and useradd has alias for addgroup and adduser, due to the
requirement constraint at my end.
    _adduser_ does invoke the passwd utility to set the user password on
creation.

Looking forward to your comments. Add to the tree if it can be.

regards,
Ashwini

Attachment: lib.patch
Description: Binary data

Attachment: passwd.c.patch
Description: Binary data

/* groupadd.c - create a new group
 *
 * Copyright 2013 Ashwini Kumar <[email protected]>
 * Copyright 2013 Kyungwan Han <[email protected]>
 *
 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/groupadd.html

USE_GROUPADD(NEWTOY(groupadd, "<1>2g#<0S", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_GROUPADD(OLDTOY(addgroup, groupadd, OPTSTR_groupadd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))

config GROUPADD
  bool "groupadd"
  default y
  help
    usage: groupadd [-S] [-g GID] [USER] GROUP

    Add a group or add a user to a group
    
      -g GID Group id
      -S     Create a system group
*/

#define FOR_groupadd
#include "toys.h"

#define GROUP_PATH        "/etc/group"
#define SECURE_GROUP_PATH "/etc/gshadow"

GLOBALS(
  long gid;
)

/* Add a new group to the system, if GID is given then that is validated
 * to be free, else a free GID is choosen by self.
 * SYSTEM IDs are considered in the range 100 ... 999
 * update_group(), updates the entries in /etc/group, /etc/gshadow files
 */
static void new_group()
{
  char *entry = NULL;
  int max = INT_MAX;

  if (toys.optflags & FLAG_g) {
    if (TT.gid > INT_MAX) error_exit("gid should be less than  '%d' ", INT_MAX);
    if (getgrgid(TT.gid)) error_exit("group '%ld' is in use", TT.gid);
  } else {
    if (toys.optflags & FLAG_S) {
      TT.gid = SYS_FIRST_ID;
      max = SYS_LAST_ID;
    } else {
      TT.gid = SYS_LAST_ID + 1; //i.e. starting from 1000
      max = 60000; // as per config file on Linux desktop
    }
    //find unused gid
    while (TT.gid <= max) {
      if (!getgrgid(TT.gid)) break;
      if (TT.gid == max) error_exit("no more free gids left");
      TT.gid++;
    }
  }

  entry = xmsprintf("%s:%s:%d:", *toys.optargs, "x", TT.gid);
  update_password(GROUP_PATH, *toys.optargs, entry);
  free(entry);
  entry = xmsprintf("%s:%s::", *toys.optargs, "!");
  update_password(SECURE_GROUP_PATH, *toys.optargs, entry);
  free(entry);
}

void groupadd_main(void)
{
  struct group *grp = NULL;
  char *entry = NULL;

  if (toys.optflags && toys.optc == 2) {
    toys.exithelp = 1;
    error_exit("options, user and group can't be together");
  }

  if (toys.optc == 2) {  //add user to group
    //toys.optargs[0]- user, toys.optargs[1] - group
    if (!getpwnam(toys.optargs[0])) 
      error_exit("user '%s' does not exist", toys.optargs[0]);
    if (!(grp = getgrnam(toys.optargs[1]))) 
      error_exit("group '%s' does not exist", toys.optargs[1]);
    if (!grp->gr_mem) entry = xmsprintf("%s", *toys.optargs);
    else {
      int i;

      for (i = 0; grp->gr_mem[i]; i++)
        if (!strcmp(grp->gr_mem[i], *toys.optargs)) return;

      entry = xstrdup("");
      for (i=0; grp->gr_mem[i]; i++) {
        entry = xrealloc(entry, strlen(entry) + strlen(grp->gr_mem[i]) + 2);
        strcat(entry, grp->gr_mem[i]);
        strcat(entry, ",");
      }
      entry = xrealloc(entry, strlen(entry) + strlen(*toys.optargs) + 1);
      strcat(entry, *toys.optargs);
    }
    update_password(GROUP_PATH, grp->gr_name, entry);
    update_password(SECURE_GROUP_PATH, grp->gr_name, entry);
    free(entry);
  } else {    //new group to be created
    /* investigate the group to be created */
    if ((grp = getgrnam(*toys.optargs))) 
      error_exit("group '%s' is in use", *toys.optargs);
    setlocale(LC_ALL, "C");
    is_valid_username(*toys.optargs);
    new_group();
  }
}
/* useradd.c - add a new user
 *
 * Copyright 2013 Ashwini Kumar <[email protected]>
 * Copyright 2013 Kyungwan Han <[email protected]>
 *
 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/useradd.html

USE_USERADD(NEWTOY(useradd, "<1>2u#<0G:s:g:h:SDH", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_USERADD(OLDTOY(adduser, useradd, OPTSTR_useradd, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))

config USERADD
  bool "useradd"
  default y
  help
    usage: useradd [-SDH] [-hDIR] [-sSHELL] [-G GRP] [-gGECOS] [-uUID] USER [GROUP]

    Create new user, or add USER to GROUP
    
    -h DIR   Home directory
    -g GECOS GECOS field
    -s SHELL Login shell
    -G GRP   Add user to existing group
    -S       Create a system user
    -D       Don't assign a password
    -H       Don't create home directory
    -u UID   User id
*/

#define FOR_useradd
#include "toys.h"

GLOBALS(
  char *dir;
  char *gecos;
  char *shell;
  char *u_grp;
  long uid;
  long gid;
)

static char* get_shell(void)    
{                               
  char *shell = getenv("SHELL");

  if (!shell) {
    struct passwd *pw;
    pw = getpwuid(getuid());
    if (pw && pw->pw_shell && pw->pw_shell[0])
      shell = pw->pw_shell;                                                                                                           
    else shell = "/bin/sh";     
  }                             
  return xstrdup(shell);        
}

/* exec_wait() function does a fork(), and exec the command,
 * waits for the child to exit and return the status to parent
 */
static int exec_wait(char **args)
{
  int status = 0;
  pid_t pid = fork();

  if (!pid) xexec(args);
  else if (pid > 0) waitpid(pid, &status, 0);
  else perror_exit("fork failed");
  return WEXITSTATUS(status);
}

/* create_copy_skel(), This function will create the home directory of the
 * user, by copying /etc/skel/ contents to /home/<username>.
 * Then change the ownership of home dir to the UID and GID of new user,
 * and Mode to 0700, i.e. rwx------ for user.
 */
static void create_copy_skel(char *skel, char *hdir)
{
  char *args[5];
  struct stat sb;

  if (toys.optflags & FLAG_H) return;

  umask(0);
  args[4] = NULL;
  if (stat(hdir, &sb)) {
    args[0] = "cp";
    args[1] = "-R";
    args[2] = skel;
    args[3] = hdir;
    // Copy /etc/skel to home dir 
    toys.exitval = exec_wait(args);

    args[0] = "chown";
    args[1] = "-R";
    args[2] = xmsprintf("%u:%u", TT.uid, TT.gid);
    args[3] = hdir;
    //Change ownership to that of UID and GID of new user
    toys.exitval = exec_wait(args);

  } else xprintf("Warning: home directory for the user already exists\n"
      "Not copying any file from skel directory into it.\n");

  if (chown(hdir, TT.uid, TT.gid) || chmod(hdir, 0700))
    perror_exit("chown/chmod failed for '%s'", hdir);
}

/* Add a new group to the system, if UID is given then that is validated
 * to be free, else a free UID is choosen by self.
 * SYSTEM IDs are considered in the range 100 ... 999
 * add_user(), add a new entry in /etc/passwd, /etc/shadow files
 */
static void new_user()
{
  struct passwd pwd;
  char *entry, *args[4];
  int max = INT_MAX;

  pwd.pw_name = *toys.optargs;
  pwd.pw_passwd = (char *)"x";
  if (toys.optflags & FLAG_g) pwd.pw_gecos = TT.gecos;
  else pwd.pw_gecos = "Linux User,";
  if (toys.optflags & FLAG_h) pwd.pw_dir = TT.dir;
  else pwd.pw_dir = xmsprintf("/home/%s", *toys.optargs);
  if (toys.optflags & FLAG_s) pwd.pw_shell = TT.shell;
  else pwd.pw_shell = get_shell();

  if (toys.optflags & FLAG_u) {
    if (TT.uid > INT_MAX) error_exit("uid should be less than  '%d' ", INT_MAX);
    if (getpwuid(TT.uid)) error_exit("user '%ld' is in use", TT.uid);
    pwd.pw_uid = TT.uid;
  } else {
    if (toys.optflags & FLAG_S) {
      TT.uid = SYS_FIRST_ID;
      max = SYS_LAST_ID;
    } else {
      TT.uid = SYS_LAST_ID + 1; //i.e. starting from 1000
      max = 60000; // as per config file on Linux desktop
    }
    //find unused uid
    while (TT.uid <= max) {
      if (!getpwuid(TT.uid)) break;
      if (TT.uid == max) error_exit("no more free uids left");
      TT.uid++;
    }
    pwd.pw_uid = TT.uid;
  }

  if (toys.optflags & FLAG_G) {
    struct group *gr = getgrnam(TT.u_grp);
    if (!gr) error_exit("The group '%s' doesn't exist", TT.u_grp);
    TT.gid = gr->gr_gid;
  } else {
    // Set the GID for the user, if not specified
    if (toys.optflags & FLAG_S) {
      TT.gid = SYS_FIRST_ID;
      max = SYS_LAST_ID;
    } else TT.gid = ((TT.uid > SYS_LAST_ID) ? TT.uid : SYS_LAST_ID + 1);
    if (getgrnam(pwd.pw_name)) error_exit("group '%s' is in use", pwd.pw_name);
    //find unused gid
    while (TT.gid <= max) {
      if (!getgrgid(TT.gid)) break;
      if (TT.gid == max) error_exit("no more free gids left");
      TT.gid++;   
    }
  }
  pwd.pw_gid = TT.gid;

  if (!(toys.optflags & FLAG_G)) {
    // Create a new group for user 
    //add group, invoke addgroup command
    args[0] = "groupadd";
    args[1] = toys.optargs[0];
    args[2] = xmsprintf("-g%ld", pwd.pw_gid);
    args[3] = NULL;
    if (exec_wait(args)) error_msg("addgroup fail");
  }

  /*add user to system 
   * 1. add an entry to /etc/passwd and /etcshadow file
   * 2. Copy /etc/skel dir contents to use home dir
   * 3. update the user passwd by running 'passwd' utility
   */

  // 1. add an entry to /etc/passwd and /etc/shadow file
  entry = xmsprintf("%s:%s:%ld:%ld:%s:%s:%s", pwd.pw_name, pwd.pw_passwd,
      pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell);
  if (update_password("/etc/passwd", pwd.pw_name, entry)) error_exit("updating passwd file failed");
  free(entry);

  if (toys.optflags & FLAG_S) 
  entry = xmsprintf("%s:!!:%u::::::", pwd.pw_name, 
      (unsigned)(time(NULL))/(24*60*60)); //passwd is not set initially
  else entry = xmsprintf("%s:!!:%u:%ld:%ld:%ld:::", pwd.pw_name, 
            (unsigned)(time(NULL))/(24*60*60), 0, 99999, 7); //passwd is not set initially
  update_password("/etc/shadow", pwd.pw_name, entry);
  free(entry);

  //2. craete home dir & copy skel dir to home
  if (!(toys.optflags & FLAG_S)) create_copy_skel("/etc/skel", pwd.pw_dir);

  //3. update the user passwd by running 'passwd' utility
  if (!(toys.optflags & FLAG_D)) {
    args[0] = "passwd";
    args[1] = pwd.pw_name;
    args[2] = NULL;
    if (exec_wait(args)) error_exit("changing user passwd failed");
  }
  if (toys.optflags & FLAG_G) {
    /*add user to the existing group, invoke addgroup command */
    args[0] = "groupadd";
    args[1] = toys.optargs[0];
    args[2] = TT.u_grp;
    args[3] = NULL;
    if (exec_wait(args)) error_exit("adding user to group Failed");
  }
}

/* Entry point for useradd feature 
 * Specifying options and User, Group at cmdline is treated as error.
 * If only 2 parameters (Non-Option) are given, then User is added to the 
 * Group
 */
void useradd_main(void)
{
  struct passwd *pwd = NULL;

  if (toys.optflags && toys.optc == 2) {
    toys.exithelp = 1;
    error_exit("options, user and group can't be together");
  }

  if (toys.optc == 2) {
    //add user to group
    //toys.optargs[0]- user, toys.optargs[1] - group
    char *args[4];
    args[0] = "groupadd";
    args[1] = toys.optargs[0];
    args[2] = toys.optargs[1];
    args[3] = NULL;
    toys.exitval = exec_wait(args);
  } else {    //new user to be created
    // investigate the user to be created
    if ((pwd = getpwnam(*toys.optargs))) 
      error_exit("user '%s' is in use", *toys.optargs);
    is_valid_username(*toys.optargs); //validate the user name
    new_user();
  }
}
/* mkpasswd.c - encrypt the given passwd using salt
 *
 * Copyright 2013 Ashwini Kumar <[email protected]>
 * Copyright 2013 Kyungwan Han <[email protected]>
 *
 * No Standard

USE_MKPASSWD(NEWTOY(mkpasswd, ">2S:m:P#=0<0", TOYFLAG_USR|TOYFLAG_BIN))

config MKPASSWD
  bool "mkpasswd"
  default y
  help
    usage: mkpasswd [OPTIONS] [PASSWORD] [SALT]

    Crypt PASSWORD using crypt(3)

    -P N    Read password from fd N
    -m TYPE Encryption method, when TYPE='help', then show the methods available
    -S SALT
*/

#define FOR_mkpasswd
#include "toys.h"
#include "lib/xregcomp.h"

GLOBALS(
  long pfd;
  char *method;
  char *salt;
)


/*
 * validate the salt provided by user.
 * the allowed character set for salt is [./A-Za-z0-9]
 */
static void is_salt_valid(char *salt)
{
  regex_t rp;
  regmatch_t rm[1];
  char *regex = "[./A-Za-z0-9]*"; //salt REGEX

  xregcomp(&rp, regex, REG_NEWLINE);

  /* compare string against pattern --  remember that patterns 
     are anchored to the beginning of the line */
  if (regexec(&rp, salt, 1, rm, 0) == 0 && rm[0].rm_so == 0 
      && rm[0].rm_eo == strlen(salt))
      return;

  error_exit("salt should be in character set [./A-Za-z0-9]");
}

void mkpasswd_main(void)
{
  int offset = 0;
  char salt[MAX_SALT_LEN] = {0,};

  if (!(toys.optflags & FLAG_m)) TT.method = "des";
  else if (!strcmp(TT.method, "help")) {
    xprintf("Available encryption methods are:\n"
        " des\n md5\n sha256\n sha512\n");
    return;
  }
  // If arguments are there, then the second argument is Salt, can be NULL also
  if ((toys.optc == 2) && !(toys.optflags & FLAG_S)) TT.salt = toys.optargs[1];

  offset= get_salt(salt, TT.method);
  if (offset == -1) error_exit("unknown encryption method");
  if (TT.salt) {
    is_salt_valid(TT.salt);
    snprintf(salt + offset, MAX_SALT_LEN - offset, "%s", TT.salt);
  }

  if (toys.optflags & FLAG_P) {
    if (dup2(TT.pfd, STDIN_FILENO) == -1) perror_exit("fd");
    close(TT.pfd);
  }

  if (!toys.optc) {
    if (isatty(STDIN_FILENO)) {
      if (read_password(toybuf, sizeof(toybuf), "Password: ")) 
        perror_exit("password read failed");
    } else {
      // read from the given FD
      int i = 0;
      while (1) {
        int ret = read(0, &toybuf[i], 1);
        if ( ret < 0 ) perror_exit("password read failed");
        else if (ret == 0 || toybuf[i] == '\n' || toybuf[i] == '\r' ||
            sizeof(toybuf) == i+1) {
          toybuf[i] = '\0';
          break;
        }
        i++;
      }
    }
  } else snprintf(toybuf, sizeof(toybuf), "%s", toys.optargs[0]);

  // encrypt & print the password
  xprintf("%s\n",crypt(toybuf, salt));
}
_______________________________________________
Toybox mailing list
[email protected]
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to