aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2013-10-16 20:41:56 -0500
committerRob Landley <rob@landley.net>2013-10-16 20:41:56 -0500
commit18ec03543c3731e1ea25182ef72c49ac5ec2d1c7 (patch)
tree7b9b35338a9f42e713833aec0a568c4713b6fcc9
parent3403742655702257d5c4972ab6ec23eb95c3fede (diff)
downloadtoybox-18ec03543c3731e1ea25182ef72c49ac5ec2d1c7.tar.gz
useradd, groupadd, and mkpasswd submitted by Ashwini Sharma.
-rw-r--r--toys/pending/groupadd.c113
-rw-r--r--toys/pending/mkpasswd.c103
-rw-r--r--toys/pending/useradd.c246
3 files changed, 462 insertions, 0 deletions
diff --git a/toys/pending/groupadd.c b/toys/pending/groupadd.c
new file mode 100644
index 00000000..ab290e54
--- /dev/null
+++ b/toys/pending/groupadd.c
@@ -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();
+ }
+}
diff --git a/toys/pending/mkpasswd.c b/toys/pending/mkpasswd.c
new file mode 100644
index 00000000..87b239e0
--- /dev/null
+++ b/toys/pending/mkpasswd.c
@@ -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));
+}
diff --git a/toys/pending/useradd.c b/toys/pending/useradd.c
new file mode 100644
index 00000000..ecfd638f
--- /dev/null
+++ b/toys/pending/useradd.c
@@ -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();
+ }
+}