randomtest fixes
[oweals/busybox.git] / loginutils / adduser.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * adduser - add users to /etc/passwd and /etc/shadow
4  *
5  * Copyright (C) 1999 by Lineo, inc. and John Beppu
6  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7  *
8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9  */
10
11 #include "libbb.h"
12
13 #define OPT_DONT_SET_PASS  (1 << 4)
14 #define OPT_SYSTEM_ACCOUNT (1 << 5)
15 #define OPT_DONT_MAKE_HOME (1 << 6)
16
17
18 /* remix */
19 /* recoded such that the uid may be passed in *p */
20 static void passwd_study(struct passwd *p)
21 {
22         int max;
23
24         if (getpwnam(p->pw_name))
25                 bb_error_msg_and_die("login '%s' is in use", p->pw_name);
26
27         if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
28                 p->pw_uid = 0;
29                 max = 999;
30         } else {
31                 p->pw_uid = 1000;
32                 max = 64999;
33         }
34
35         /* check for a free uid (and maybe gid) */
36         while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) {
37                 p->pw_uid++;
38                 if (p->pw_uid > max)
39                         bb_error_msg_and_die("no free uids left");
40         }
41
42         if (p->pw_gid == (gid_t)-1) {
43                 p->pw_gid = p->pw_uid; /* new gid = uid */
44                 if (getgrnam(p->pw_name))
45                         bb_error_msg_and_die("group name '%s' is in use", p->pw_name);
46         }
47 }
48
49 static void addgroup_wrapper(struct passwd *p)
50 {
51         char *cmd;
52
53         cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
54         system(cmd);
55         free(cmd);
56 }
57
58 static void passwd_wrapper(const char *login) NORETURN;
59
60 static void passwd_wrapper(const char *login)
61 {
62         static const char prog[] ALIGN1 = "passwd";
63
64         BB_EXECLP(prog, prog, login, NULL);
65         bb_error_msg_and_die("cannot execute %s, you must set password manually", prog);
66 }
67
68 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
69 static const char adduser_longopts[] ALIGN1 =
70                 "home\0"                Required_argument "h"
71                 "gecos\0"               Required_argument "g"
72                 "shell\0"               Required_argument "s"
73                 "ingroup\0"             Required_argument "G"
74                 "disabled-password\0"   No_argument       "D"
75                 "empty-password\0"      No_argument       "D"
76                 "system\0"              No_argument       "S"
77                 "no-create-home\0"      No_argument       "H"
78                 ;
79 #endif
80
81 /*
82  * adduser will take a login_name as its first parameter.
83  * home, shell, gecos:
84  * can be customized via command-line parameters.
85  */
86 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
87 int adduser_main(int argc UNUSED_PARAM, char **argv)
88 {
89         struct passwd pw;
90         const char *usegroup = NULL;
91         FILE *file;
92 #if ENABLE_FEATURE_SHADOWPASSWDS
93         int fd;
94 #endif
95
96 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
97         applet_long_options = adduser_longopts;
98 #endif
99
100         /* got root? */
101         if (geteuid()) {
102                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
103         }
104
105         pw.pw_gecos = (char *)"Linux User,,,";
106         pw.pw_shell = (char *)DEFAULT_SHELL;
107         pw.pw_dir = NULL;
108
109         /* exactly one non-option arg */
110         opt_complementary = "=1";
111         getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
112         argv += optind;
113
114         /* fill in the passwd struct */
115         pw.pw_name = argv[0];
116         die_if_bad_username(pw.pw_name);
117         if (!pw.pw_dir) {
118                 /* create string for $HOME if not specified already */
119                 pw.pw_dir = xasprintf("/home/%s", argv[0]);
120         }
121         pw.pw_passwd = (char *)"x";
122         pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */
123
124         /* make sure everything is kosher and setup uid && maybe gid */
125         passwd_study(&pw);
126
127         /* add to passwd */
128         file = xfopen(bb_path_passwd_file, "a");
129         //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */
130         if (putpwent(&pw, file) != 0) {
131                 bb_perror_nomsg_and_die();
132         }
133         /* do fclose even if !ENABLE_FEATURE_CLEAN_UP.
134          * We will exec passwd, files must be flushed & closed before that! */
135         fclose(file);
136
137 #if ENABLE_FEATURE_SHADOWPASSWDS
138         /* add to shadow if necessary */
139         /* fopen(..., "a"); would create shadow file, which is wrong.
140          * If shadow file doesn't exist, admin probably does not want it */
141         fd = open_or_warn(bb_path_shadow_file, O_WRONLY | O_APPEND);
142         if (fd >= 0) {
143                 char *s = xasprintf("%s:!:%u:0:99999:7:::\n",
144                                 pw.pw_name,             /* username */
145                                 (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */
146                                 /*0,*/                  /* sp->sp_min */
147                                 /*99999,*/              /* sp->sp_max */
148                                 /*7*/                   /* sp->sp_warn */
149                 );
150                 xwrite_str(fd, s);
151                 close(fd);
152         }
153 #endif
154
155         /* add to group */
156         /* addgroup should be responsible for dealing w/ gshadow */
157         /* if using a pre-existing group, don't create one */
158         if (!usegroup)
159                 addgroup_wrapper(&pw);
160
161         /* Clear the umask for this process so it doesn't
162          * screw up the permissions on the mkdir and chown. */
163         umask(0);
164         if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
165                 /* Set the owner and group so it is owned by the new user,
166                    then fix up the permissions to 2755. Can't do it before
167                    since chown will clear the setgid bit */
168                 if (mkdir(pw.pw_dir, 0755)
169                  || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid)
170                  || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */
171                 ) {
172                         bb_simple_perror_msg(pw.pw_dir);
173                 }
174         }
175
176         if (!(option_mask32 & OPT_DONT_SET_PASS)) {
177                 /* interactively set passwd */
178                 passwd_wrapper(pw.pw_name);
179         }
180
181         return 0;
182 }