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 
8 USE_USERADD(NEWTOY(useradd, "<1>2u#<0G:s:g:h:SDH", TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
9 USE_USERADD(OLDTOY(adduser, useradd, TOYFLAG_NEEDROOT|TOYFLAG_UMASK|TOYFLAG_SBIN))
10 
11 config 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 
GLOBALS(char * dir;char * gecos;char * shell;char * u_grp;long uid;long gid;)32 GLOBALS(
33   char *dir;
34   char *gecos;
35   char *shell;
36   char *u_grp;
37   long uid;
38 
39   long gid;
40 )
41 
42 void 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) {
50       toys.exithelp = 1;
51       error_exit("options with USER GROUP");
52     }
53     xexec((char *[]){"groupadd", toys.optargs[0], toys.optargs[1], 0});
54   }
55 
56   // Sanity check user to add
57   if (s[strcspn(s, ":/\n")] || strlen(s) > LOGIN_NAME_MAX)
58     error_exit("bad username");
59   // race condition: two adds at same time?
60   if (getpwnam(s)) error_exit("'%s' in use", s);
61 
62   // Add a new group to the system, if UID is given then that is validated
63   // to be free, else a free UID is choosen by self.
64   // SYSTEM IDs are considered in the range 100 ... 999
65   // add_user(), add a new entry in /etc/passwd, /etc/shadow files
66 
67   pwd.pw_name = s;
68   pwd.pw_passwd = "x";
69   pwd.pw_gecos = TT.gecos ? TT.gecos : "Linux User,";
70   pwd.pw_dir = TT.dir ? TT.dir : xmprintf("/home/%s", *toys.optargs);
71 
72   if (!TT.shell) {
73     TT.shell = getenv("SHELL");
74 
75     if (!TT.shell) {
76       struct passwd *pw = getpwuid(getuid());
77 
78       if (pw && pw->pw_shell && *pw->pw_shell) TT.shell = xstrdup(pw->pw_shell);
79       else TT.shell = "/bin/sh";
80     }
81   }
82   pwd.pw_shell = TT.shell;
83 
84   if (toys.optflags & FLAG_u) {
85     if (TT.uid > INT_MAX) error_exit("bad uid");
86     if (getpwuid(TT.uid)) error_exit("uid '%ld' in use", TT.uid);
87   } else {
88     if (toys.optflags & FLAG_S) TT.uid = CFG_TOYBOX_UID_SYS;
89     else TT.uid = CFG_TOYBOX_UID_USR;
90     //find unused uid
91     while (getpwuid(TT.uid)) TT.uid++;
92   }
93   pwd.pw_uid = TT.uid;
94 
95   if (toys.optflags & FLAG_G) TT.gid = xgetgrnam(TT.u_grp)->gr_gid;
96   else {
97     // Set the GID for the user, if not specified
98     if (toys.optflags & FLAG_S) TT.gid = CFG_TOYBOX_UID_SYS;
99     else TT.gid = CFG_TOYBOX_UID_USR;
100     if (getgrnam(pwd.pw_name)) error_exit("group '%s' in use", pwd.pw_name);
101     //find unused gid
102     while (getgrgid(TT.gid)) TT.gid++;
103   }
104   pwd.pw_gid = TT.gid;
105 
106   // Create a new group for user
107   if (!(toys.optflags & FLAG_G)) {
108     char *s = xmprintf("-g%ld", pwd.pw_gid);
109 
110     if (xrun((char *[]){"groupadd", *toys.optargs, s, 0}))
111       error_msg("addgroup -g%ld fail", pwd.pw_gid);
112     free(s);
113   }
114 
115   /*add user to system
116    * 1. add an entry to /etc/passwd and /etcshadow file
117    * 2. Copy /etc/skel dir contents to use home dir
118    * 3. update the user passwd by running 'passwd' utility
119    */
120 
121   // 1. add an entry to /etc/passwd and /etc/shadow file
122   entry = xmprintf("%s:%s:%ld:%ld:%s:%s:%s", pwd.pw_name, pwd.pw_passwd,
123       pwd.pw_uid, pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell);
124   if (update_password("/etc/passwd", pwd.pw_name, entry)) error_exit("updating passwd file failed");
125   free(entry);
126 
127   if (toys.optflags & FLAG_S)
128   entry = xmprintf("%s:!!:%u::::::", pwd.pw_name,
129       (unsigned)(time(NULL))/(24*60*60)); //passwd is not set initially
130   else entry = xmprintf("%s:!!:%u:%ld:%ld:%ld:::", pwd.pw_name,
131             (unsigned)(time(NULL))/(24*60*60), 0, 99999, 7); //passwd is not set initially
132   update_password("/etc/shadow", pwd.pw_name, entry);
133   free(entry);
134 
135   // create home dir & copy skel dir to home
136   if (!(toys.optflags & (FLAG_S|FLAG_H))) {
137     char *skel = "/etc/skel", *p = pwd.pw_dir;
138 
139     // Copy and change ownership
140     if (access(p, F_OK)) {
141       if (!access(skel, R_OK))
142         toys.exitval = xrun((char *[]){"cp", "-R", skel, p, 0});
143       else toys.exitval = xrun((char *[]){"mkdir", "-p", p, 0});
144       if (!toys.exitval)
145         toys.exitval |= xrun((char *[]){"chown", "-R",
146           xmprintf("%u:%u", TT.uid, TT.gid), p, 0});
147       wfchmodat(AT_FDCWD, p, 0700);
148     } else fprintf(stderr, "'%s' exists, not copying '%s'", p, skel);
149   }
150 
151   //3. update the user passwd by running 'passwd' utility
152   if (!(toys.optflags & FLAG_D))
153     if (xrun((char *[]){"passwd", pwd.pw_name, 0})) error_exit("passwd");
154 
155   if (toys.optflags & FLAG_G) {
156     /*add user to the existing group, invoke addgroup command */
157     if (xrun((char *[]){"groupadd", *toys.optargs, TT.u_grp, 0}))
158       error_exit("groupadd");
159   }
160 }
161