/* useradd.c - add a new user * * Copyright 2013 Ashwini Kumar * Copyright 2013 Kyungwan Han * * 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/. * 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]; pwd.pw_name = *toys.optargs; pwd.pw_passwd = "x"; pwd.pw_gecos = (toys.optflags & FLAG_g) ? TT.gecos : "Linux User,"; pwd.pw_dir = (toys.optflags & FLAG_h) ? TT.dir : xmprintf("/home/%s", *toys.optargs); pwd.pw_shell = (toys.optflags & FLAG_s) ? TT.shell : get_shell(); if (toys.optflags & FLAG_u) { if (TT.uid > INT_MAX) error_exit("bad uid"); if (getpwuid(TT.uid)) error_exit("uid '%ld' in use", TT.uid); } else { if (toys.optflags & FLAG_S) TT.uid = CFG_TOYBOX_UID_SYS; else TT.uid = CFG_TOYBOX_UID_USR; //find unused uid while (getpwuid(TT.uid)) TT.uid++; } pwd.pw_uid = TT.uid; if (toys.optflags & FLAG_G) TT.gid = xgetgrnam(TT.u_grp)->gr_gid; else { // Set the GID for the user, if not specified if (toys.optflags & FLAG_S) TT.gid = CFG_TOYBOX_UID_SYS; else TT.gid = CFG_TOYBOX_UID_USR; if (getgrnam(pwd.pw_name)) error_exit("group '%s' in use", pwd.pw_name); //find unused gid while (getgrgid(TT.gid)) 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(); } }