1 /* vi: set sw=4 ts=4: */
3 * adduser - add users to /etc/passwd and /etc/shadow
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 //config:config ADDUSER
11 //config: bool "adduser (15 kb)"
14 //config: Utility for creating a new user account.
16 //config:config FEATURE_ADDUSER_LONG_OPTIONS
17 //config: bool "Enable long options"
19 //config: depends on ADDUSER && LONG_OPTS
21 //config:config FEATURE_CHECK_NAMES
22 //config: bool "Enable sanity check on user/group names in adduser and addgroup"
24 //config: depends on ADDUSER || ADDGROUP
26 //config: Enable sanity check on user and group names in adduser and addgroup.
27 //config: To avoid problems, the user or group name should consist only of
28 //config: letters, digits, underscores, periods, at signs and dashes,
29 //config: and not start with a dash (as defined by IEEE Std 1003.1-2001).
30 //config: For compatibility with Samba machine accounts "$" is also supported
31 //config: at the end of the user or group name.
33 //config:config LAST_ID
34 //config: int "Last valid uid or gid for adduser and addgroup"
35 //config: depends on ADDUSER || ADDGROUP
36 //config: default 60000
38 //config: Last valid uid or gid for adduser and addgroup
40 //config:config FIRST_SYSTEM_ID
41 //config: int "First valid system uid or gid for adduser and addgroup"
42 //config: depends on ADDUSER || ADDGROUP
43 //config: range 0 LAST_ID
46 //config: First valid system uid or gid for adduser and addgroup
48 //config:config LAST_SYSTEM_ID
49 //config: int "Last valid system uid or gid for adduser and addgroup"
50 //config: depends on ADDUSER || ADDGROUP
51 //config: range FIRST_SYSTEM_ID LAST_ID
54 //config: Last valid system uid or gid for adduser and addgroup
56 //applet:IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP))
58 //kbuild:lib-$(CONFIG_ADDUSER) += adduser.o
60 //usage:#define adduser_trivial_usage
61 //usage: "[OPTIONS] USER [GROUP]"
62 //usage:#define adduser_full_usage "\n\n"
63 //usage: "Create new user, or add USER to GROUP\n"
64 //usage: "\n -h DIR Home directory"
65 //usage: "\n -g GECOS GECOS field"
66 //usage: "\n -s SHELL Login shell"
67 //usage: "\n -G GRP Group"
68 //usage: "\n -S Create a system user"
69 //usage: "\n -D Don't assign a password"
70 //usage: "\n -H Don't create home directory"
71 //usage: "\n -u UID User id"
72 //usage: "\n -k SKEL Skeleton directory (/etc/skel)"
76 #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
77 #error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
79 #if CONFIG_LAST_ID < CONFIG_LAST_SYSTEM_ID
80 #error Bad LAST_ID or LAST_SYSTEM_ID in .config
84 /* #define OPT_HOME (1 << 0) */ /* unused */
85 /* #define OPT_GECOS (1 << 1) */ /* unused */
86 #define OPT_SHELL (1 << 2)
87 #define OPT_GID (1 << 3)
88 #define OPT_DONT_SET_PASS (1 << 4)
89 #define OPT_SYSTEM_ACCOUNT (1 << 5)
90 #define OPT_DONT_MAKE_HOME (1 << 6)
91 #define OPT_UID (1 << 7)
92 #define OPT_SKEL (1 << 8)
95 /* recoded such that the uid may be passed in *p */
96 static void passwd_study(struct passwd *p)
98 int max = CONFIG_LAST_ID;
100 if (getpwnam(p->pw_name)) {
101 bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name);
102 /* this format string is reused in adduser and addgroup */
105 if (!(option_mask32 & OPT_UID)) {
106 if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
107 p->pw_uid = CONFIG_FIRST_SYSTEM_ID;
108 max = CONFIG_LAST_SYSTEM_ID;
110 p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1;
113 /* check for a free uid (and maybe gid) */
114 while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) {
115 if (option_mask32 & OPT_UID) {
116 /* -u N, cannot pick uid other than N: error */
117 bb_error_msg_and_die("%s '%s' in use", "uid", itoa(p->pw_uid));
118 /* this format string is reused in adduser and addgroup */
120 if (p->pw_uid == max) {
121 bb_error_msg_and_die("no %cids left", 'u');
122 /* this format string is reused in adduser and addgroup */
127 if (p->pw_gid == (gid_t)-1) {
128 p->pw_gid = p->pw_uid; /* new gid = uid */
129 if (getgrnam(p->pw_name)) {
130 bb_error_msg_and_die("%s '%s' in use", "group", p->pw_name);
131 /* this format string is reused in adduser and addgroup */
136 static int addgroup_wrapper(struct passwd *p, const char *group_name)
140 argv[0] = (char*)"addgroup";
142 /* Add user to existing group */
143 argv[1] = (char*)"--";
144 argv[2] = p->pw_name;
145 argv[3] = (char*)group_name;
148 /* Add user to his own group with the first free gid
149 * found in passwd_study.
151 #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP
152 /* We try to use --gid, not -g, because "standard" addgroup
153 * has no short option -g, it has only long --gid.
155 argv[1] = (char*)"--gid";
157 /* Breaks if system in fact does NOT use busybox addgroup */
158 argv[1] = (char*)"-g";
160 argv[2] = utoa(p->pw_gid);
161 argv[3] = (char*)"--";
162 argv[4] = p->pw_name;
166 return spawn_and_wait(argv);
169 static void passwd_wrapper(const char *login_name) NORETURN;
171 static void passwd_wrapper(const char *login_name)
173 BB_EXECLP("passwd", "passwd", "--", login_name, NULL);
174 bb_error_msg_and_die("can't execute passwd, you must set password manually");
177 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
178 static const char adduser_longopts[] ALIGN1 =
179 "home\0" Required_argument "h"
180 "gecos\0" Required_argument "g"
181 "shell\0" Required_argument "s"
182 "ingroup\0" Required_argument "G"
183 "disabled-password\0" No_argument "D"
184 "empty-password\0" No_argument "D"
185 "system\0" No_argument "S"
186 "no-create-home\0" No_argument "H"
187 "uid\0" Required_argument "u"
188 "skel\0" Required_argument "k"
193 * adduser will take a login_name as its first parameter.
194 * home, shell, gecos:
195 * can be customized via command-line parameters.
197 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
198 int adduser_main(int argc UNUSED_PARAM, char **argv)
201 const char *usegroup = NULL;
205 const char *skel = "/etc/skel";
207 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
208 applet_long_options = adduser_longopts;
213 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
216 pw.pw_gecos = (char *)"Linux User,,,";
217 /* We assume that newly created users "inherit" root's shell setting */
218 pw.pw_shell = (char *)get_shell_name();
221 /* at least one and at most two non-option args */
222 /* disable interactive passwd for system accounts */
223 opt_complementary = "-1:?2:SD";
224 opts = getopt32(argv, "h:g:s:G:DSHu:k:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid, &skel);
226 pw.pw_uid = xatou_range(uid, 0, CONFIG_LAST_ID);
229 pw.pw_name = argv[0];
231 if (!opts && argv[1]) {
232 /* if called with two non-option arguments, adduser
233 * will add an existing user to an existing group.
235 return addgroup_wrapper(&pw, argv[1]);
238 /* fill in the passwd struct */
239 die_if_bad_username(pw.pw_name);
241 /* create string for $HOME if not specified already */
242 pw.pw_dir = xasprintf("/home/%s", argv[0]);
244 pw.pw_passwd = (char *)"x";
245 if (opts & OPT_SYSTEM_ACCOUNT) {
247 usegroup = "nogroup";
249 if (!(opts & OPT_SHELL)) {
250 pw.pw_shell = (char *) "/bin/false";
253 pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */
255 /* make sure everything is kosher and setup uid && maybe gid */
258 p = xasprintf("x:%u:%u:%s:%s:%s",
259 (unsigned) pw.pw_uid, (unsigned) pw.pw_gid,
260 pw.pw_gecos, pw.pw_dir, pw.pw_shell);
261 if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) {
264 if (ENABLE_FEATURE_CLEAN_UP)
266 #if ENABLE_FEATURE_SHADOWPASSWDS
267 /* /etc/shadow fields:
269 * 2. encrypted password
270 * 3. last password change (unix date (unix time/24*60*60))
271 * 4. minimum days required between password changes
272 * 5. maximum days password is valid
273 * 6. days before password is to expire that user is warned
274 * 7. days after password expires that account is disabled
275 * 8. unix date when login expires (i.e. when it may no longer be used)
277 /* fields: 2 3 4 5 6 78 */
278 p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL)) / (24*60*60));
279 /* ignore errors: if file is missing we suppose admin doesn't want it */
280 update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL);
281 if (ENABLE_FEATURE_CLEAN_UP)
286 addgroup_wrapper(&pw, usegroup);
288 /* clear the umask for this process so it doesn't
289 * screw up the permissions on the mkdir and chown. */
291 if (!(opts & OPT_DONT_MAKE_HOME)) {
292 /* set the owner and group so it is owned by the new user,
293 * then fix up the permissions to 2755. Can't do it before
294 * since chown will clear the setgid bit */
295 int mkdir_err = mkdir(pw.pw_dir, 0755);
296 if (mkdir_err == 0) {
297 /* New home. Copy /etc/skel to it */
298 const char *args[] = {
301 xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid),
305 /* Be silent on any errors (like: no /etc/skel) */
306 if (!(opts & OPT_SKEL))
307 logmode = LOGMODE_NONE;
308 copy_file(skel, pw.pw_dir, FILEUTILS_RECUR);
309 logmode = LOGMODE_STDIO;
310 chown_main(4, (char**)args);
312 if ((mkdir_err != 0 && errno != EEXIST)
313 || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) != 0
314 || chmod(pw.pw_dir, 02755) != 0 /* set setgid bit on homedir */
316 bb_simple_perror_msg(pw.pw_dir);
320 if (!(opts & OPT_DONT_SET_PASS)) {
321 /* interactively set passwd */
322 passwd_wrapper(pw.pw_name);