1/* useradd.c - add a new user
2 *
3 * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 *
6 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/useradd.html
7
8USE_USERADD(NEWTOY(useradd, "<1>2u#<0G:s:g:h:SDH", TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
9USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
10
11config USERADD
12  bool "useradd"
13  default n
14  help
15    usage: useradd [-SDH] [-h DIR] [-s SHELL] [-G GRP] [-g NAME] [-u UID] USER [GROUP]
16
17    Create new user, or add USER to GROUP
18
19    -D       Don't assign a password
20    -g NAME  Real name
21    -G GRP   Add user to existing group
22    -h DIR   Home directory
23    -H       Don't create home directory
24    -s SHELL Login shell
25    -S       Create a system user
26    -u UID   User id
27*/
28
29#define FOR_useradd
30#include "toys.h"
31
32GLOBALS(
33  char *dir;
34  char *gecos;
35  char *shell;
36  char *u_grp;
37  long uid;
38
39  long gid;
40)
41
42void useradd_main(void)
43{
44  char *s = *toys.optargs, *entry;
45  struct passwd pwd;
46
47  // Act like groupadd?
48  if (toys.optc == 2) {
49    if (toys.optflags) help_exit("options with USER GROUP");
50    xexec((char *[]){"groupadd", toys.optargs[0], toys.optargs[1], 0});
51  }
52
53  // Sanity check user to add
54  if (s[strcspn(s, ":/\n")] || strlen(s) > LOGIN_NAME_MAX)
55    error_exit("bad username");
56  // race condition: two adds at same time?
57  if (getpwnam(s)) error_exit("'%s' in use", s);
58
59  // Add a new group to the system, if UID is given then that is validated
60  // to be free, else a free UID is choosen by self.
61  // SYSTEM IDs are considered in the range 100 ... 999
62  // add_user(), add a new entry in /etc/passwd, /etc/shadow files
63
64  pwd.pw_name = s;
65  pwd.pw_passwd = "x";
66  pwd.pw_gecos = TT.gecos ? TT.gecos : "Linux User,";
67  pwd.pw_dir = TT.dir ? TT.dir : xmprintf("/home/%s", *toys.optargs);
68
69  if (!TT.shell) {
70    TT.shell = getenv("SHELL");
71
72    if (!TT.shell) {
73      struct passwd *pw = getpwuid(getuid());
74
75      if (pw && pw->pw_shell && *pw->pw_shell) TT.shell = xstrdup(pw->pw_shell);
76      else TT.shell = "/bin/sh";
77    }
78  }
79  pwd.pw_shell = TT.shell;
80
81  if (toys.optflags & FLAG_u) {
82    if (TT.uid > INT_MAX) error_exit("bad uid");
83    if (getpwuid(TT.uid)) error_exit("uid '%ld' in use", TT.uid);
84  } else {
85    if (toys.optflags & FLAG_S) TT.uid = CFG_TOYBOX_UID_SYS;
86    else TT.uid = CFG_TOYBOX_UID_USR;
87    //find unused uid
88    while (getpwuid(TT.uid)) TT.uid++;
89  }
90  pwd.pw_uid = TT.uid;
91
92  if (toys.optflags & FLAG_G) TT.gid = xgetgrnam(TT.u_grp)->gr_gid;
93  else {
94    // Set the GID for the user, if not specified
95    if (toys.optflags & FLAG_S) TT.gid = CFG_TOYBOX_UID_SYS;
96    else TT.gid = CFG_TOYBOX_UID_USR;
97    if (getgrnam(pwd.pw_name)) error_exit("group '%s' in use", pwd.pw_name);
98    //find unused gid
99    while (getgrgid(TT.gid)) TT.gid++;
100  }
101  pwd.pw_gid = TT.gid;
102
103  // Create a new group for user
104  if (!(toys.optflags & FLAG_G)) {
105    char *s = xmprintf("-g%ld", (long)pwd.pw_gid);
106
107    if (xrun((char *[]){"groupadd", *toys.optargs, s, 0}))
108      error_msg("addgroup -g%ld fail", (long)pwd.pw_gid);
109    free(s);
110  }
111
112  /*add user to system
113   * 1. add an entry to /etc/passwd and /etcshadow file
114   * 2. Copy /etc/skel dir contents to use home dir
115   * 3. update the user passwd by running 'passwd' utility
116   */
117
118  // 1. add an entry to /etc/passwd and /etc/shadow file
119  entry = xmprintf("%s:%s:%ld:%ld:%s:%s:%s", pwd.pw_name, pwd.pw_passwd,
120      (long)pwd.pw_uid, (long)pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir,
121      pwd.pw_shell);
122  if (update_password("/etc/passwd", pwd.pw_name, entry)) error_exit("updating passwd file failed");
123  free(entry);
124
125  if (toys.optflags & FLAG_S)
126  entry = xmprintf("%s:!!:%u::::::", pwd.pw_name,
127      (unsigned)(time(NULL))/(24*60*60)); //passwd is not set initially
128  else entry = xmprintf("%s:!!:%u:0:99999:7:::", pwd.pw_name,
129            (unsigned)(time(0))/(24*60*60)); //passwd is not set initially
130  update_password("/etc/shadow", pwd.pw_name, entry);
131  free(entry);
132
133  // create home dir & copy skel dir to home
134  if (!(toys.optflags & (FLAG_S|FLAG_H))) {
135    char *skel = "/etc/skel", *p = pwd.pw_dir;
136
137    // Copy and change ownership
138    if (access(p, F_OK)) {
139      if (!access(skel, R_OK))
140        toys.exitval = xrun((char *[]){"cp", "-R", skel, p, 0});
141      else toys.exitval = xrun((char *[]){"mkdir", "-p", p, 0});
142      if (!toys.exitval)
143        toys.exitval |= xrun((char *[]){"chown", "-R",
144          xmprintf("%lu:%lu", TT.uid, TT.gid), p, 0});
145      wfchmodat(AT_FDCWD, p, 0700);
146    } else fprintf(stderr, "'%s' exists, not copying '%s'", p, skel);
147  }
148
149  //3. update the user passwd by running 'passwd' utility
150  if (!(toys.optflags & FLAG_D))
151    if (xrun((char *[]){"passwd", pwd.pw_name, 0})) error_exit("passwd");
152
153  if (toys.optflags & FLAG_G) {
154    /*add user to the existing group, invoke addgroup command */
155    if (xrun((char *[]){"groupadd", *toys.optargs, TT.u_grp, 0}))
156      error_exit("groupadd");
157  }
158}
159