view toys/pending/useradd.c @ 1363:e65f9a9ba62d draft

Cleanup pass on mkpasswd.c
author Rob Landley <rob@landley.net>
date Wed, 25 Jun 2014 22:54:59 -0500
parents c7bf4541d5be
children ed28d4d9558d
line wrap: on
line source

/* useradd.c - add a new user
 *
 * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
 *
 * 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 n
  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 = xfork();

  if (!pid) xexec(args);
  else waitpid(pid, &status, 0);

  return WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127;
}

/* 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] = xmprintf("%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 = xmprintf("/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] = xmprintf("-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 = xmprintf("%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 = xmprintf("%s:!!:%u::::::", pwd.pw_name, 
      (unsigned)(time(NULL))/(24*60*60)); //passwd is not set initially
  else entry = xmprintf("%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();
  }
}