changeset 1091:c4d4a6e7473f draft

useradd, groupadd, and mkpasswd submitted by Ashwini Sharma.
author Rob Landley <rob@landley.net>
date Wed, 16 Oct 2013 20:41:56 -0500
parents 7c62d5db4484
children 1a5c7092afbf
files toys/pending/groupadd.c toys/pending/mkpasswd.c toys/pending/useradd.c
diffstat 3 files changed, 462 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/groupadd.c	Wed Oct 16 20:41:56 2013 -0500
@@ -0,0 +1,113 @@
+/* groupadd.c - create a new group
+ *
+ * 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/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 n
+  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();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/mkpasswd.c	Wed Oct 16 20:41:56 2013 -0500
@@ -0,0 +1,103 @@
+/* mkpasswd.c - encrypt the given passwd using salt
+ *
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
+ * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ *
+ * No Standard
+
+USE_MKPASSWD(NEWTOY(mkpasswd, ">2S:m:P#=0<0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config MKPASSWD
+  bool "mkpasswd"
+  default n
+  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));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/useradd.c	Wed Oct 16 20:41:56 2013 -0500
@@ -0,0 +1,246 @@
+/* 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 = 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();
+  }
+}