blob: 0a79064be49fff50d0b0a9d4338339769e86289a [file] [log] [blame]
/* 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();
}
}