Bertrand Baudet writes:
[platform/upstream/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  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include "busybox.h"
40
41
42
43 /* structs __________________________ */
44
45 typedef struct {
46         uid_t u;
47         gid_t g;
48 } Id;
49
50 /* data _____________________________ */
51
52 /* defaults : should this be in an external file? */
53 static const char default_passwd[] = "x";
54 static const char default_gecos[] = "Linux User,,,";
55 static const char default_home_prefix[] = "/home";
56
57 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
58 /* shadow in use? */
59 static int shadow_enabled = 0;
60 #endif
61
62 /* remix */
63 /* EDR recoded such that the uid may be passed in *p */
64 static int passwd_study(const char *filename, struct passwd *p)
65 {
66         struct passwd *pw;
67         FILE *passwd;
68
69         const int min = 500;
70         const int max = 65000;
71
72         passwd = bb_wfopen(filename, "r");
73         if (!passwd)
74                 return 4;
75
76         /* EDR if uid is out of bounds, set to min */
77         if ((p->pw_uid > max) || (p->pw_uid < min))
78                 p->pw_uid = min;
79
80         /* stuff to do:
81          * make sure login isn't taken;
82          * find free uid and gid;
83          */
84         while ((pw = fgetpwent(passwd))) {
85                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
86                         /* return 0; */
87                         return 1;
88                 }
89                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
90                         && (pw->pw_uid >= min)) {
91                         p->pw_uid = pw->pw_uid + 1;
92                 }
93         }
94
95         if (p->pw_gid == 0) {
96                 /* EDR check for an already existing gid */
97                 while (getgrgid(p->pw_uid) != NULL)
98                         p->pw_uid++;
99
100                 /* EDR also check for an existing group definition */
101                 if (getgrnam(p->pw_name) != NULL)
102                         return 3;
103
104                 /* EDR create new gid always = uid */
105                 p->pw_gid = p->pw_uid;
106         }
107
108         /* EDR bounds check */
109         if ((p->pw_uid > max) || (p->pw_uid < min))
110                 return 2;
111
112         /* return 1; */
113         return 0;
114 }
115
116 static void addgroup_wrapper(const char *login, gid_t gid)
117 {
118         char *cmd;
119
120         bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
121         system(cmd);
122         free(cmd);
123 }
124
125 static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
126
127 static void passwd_wrapper(const char *login)
128 {
129         static const char prog[] = "passwd";
130         execlp(prog, prog, login, NULL);
131         bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
132 }
133
134 /* putpwent(3) remix */
135 static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
136 {
137         FILE *passwd;
138         int r;
139 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
140         FILE *shadow;
141         struct spwd *sp;
142 #endif
143         int new_group = 1;
144
145         /* if using a pre-existing group, don't create one */
146         if (p->pw_gid != 0)
147                 new_group = 0;
148
149         /* make sure everything is kosher and setup uid && gid */
150         passwd = bb_wfopen(filename, "a");
151         if (passwd == NULL) {
152                 return 1;
153         }
154         fseek(passwd, 0, SEEK_END);
155
156         /* if (passwd_study(filename, p) == 0) { */
157         r = passwd_study(filename, p);
158         if (r) {
159                 if (r == 1)
160                         bb_error_msg("%s: login already in use", p->pw_name);
161                 else if (r == 2)
162                         bb_error_msg("illegal uid or no uids left");
163                 else if (r == 3)
164                         bb_error_msg("group name %s already in use", p->pw_name);
165                 else
166                         bb_error_msg("generic error.");
167                 return 1;
168         }
169
170         /* add to passwd */
171         if (putpwent(p, passwd) == -1) {
172                 return 1;
173         }
174         fclose(passwd);
175
176 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
177         /* add to shadow if necessary */
178         if (shadow_enabled) {
179                 shadow = bb_wfopen(bb_path_shadow_file, "a");
180                 if (shadow == NULL) {
181                         return 1;
182                 }
183                 fseek(shadow, 0, SEEK_END);
184                 sp = pwd_to_spwd(p);
185                 sp->sp_max = 99999;             /* debianish */
186                 sp->sp_warn = 7;
187                 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
188                                 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
189                                 sp->sp_warn);
190                 fclose(shadow);
191         }
192 #endif
193
194         if (new_group) {
195                 /* add to group */
196                 /* addgroup should be responsible for dealing w/ gshadow */
197                 addgroup_wrapper(p->pw_name, p->pw_gid);
198         }
199
200         /* Clear the umask for this process so it doesn't
201          * * screw up the permissions on the mkdir and chown. */
202         umask(0);
203
204         if (makehome) {
205                 /* mkdir */
206                 if (mkdir(p->pw_dir, 0755)) {
207                         bb_perror_msg("%s", p->pw_dir);
208                 }
209                 /* Set the owner and group so it is owned by the new user. */
210                 if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
211                         bb_perror_msg("%s", p->pw_dir);
212                 }
213                 /* Now fix up the permissions to 2755. Can't do it before now
214                  * since chown will clear the setgid bit */
215                 if (chmod(p->pw_dir, 02755)) {
216                         bb_perror_msg("%s", p->pw_dir);
217                 }
218         }
219
220         if (setpass) {
221                 /* interactively set passwd */
222                 passwd_wrapper(p->pw_name);
223         }
224
225         return 0;
226 }
227
228
229 /* return current uid (root is always uid == 0, right?) */
230 #ifndef CONFIG_ADDGROUP
231 static inline void if_i_am_not_root(void)
232 #else
233 void if_i_am_not_root(void)
234 #endif
235 {
236         if (geteuid()) {
237                 bb_error_msg_and_die( "Only root may add a user or group to the system.");
238         }
239 }
240
241 #define SETPASS                         (1 << 4)
242 #define MAKEHOME                        (1 << 6)
243
244 /*
245  * adduser will take a login_name as its first parameter.
246  *
247  * home
248  * shell
249  * gecos
250  *
251  * can be customized via command-line parameters.
252  * ________________________________________________________________________ */
253 int adduser_main(int argc, char **argv)
254 {
255         struct passwd pw;
256         const char *login;
257         const char *gecos = default_gecos;
258         const char *home = NULL;
259         const char *shell = DEFAULT_SHELL;
260         const char *usegroup = NULL;
261         int flags;
262         int setpass = 1;
263         int makehome = 1;
264
265         /* init */
266         if (argc < 2) {
267                 bb_show_usage();
268         }
269         /* get args */
270         flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
271
272         if (flags & SETPASS) {
273                 setpass = 0;
274         }
275         if (flags & MAKEHOME) {
276                 makehome = 0;
277         }
278
279         /* got root? */
280         if_i_am_not_root();
281
282         /* get login */
283         if (optind >= argc) {
284                 bb_error_msg_and_die( "no user specified");
285         }
286         login = argv[optind];
287
288         /* create string for $HOME if not specified already */
289         if (!home) {
290                 home = concat_path_file(default_home_prefix, login);
291         }
292 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
293         /* is /etc/shadow in use? */
294         shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
295 #endif
296
297         /* create a passwd struct */
298         pw.pw_name = (char *)login;
299         pw.pw_passwd = (char *)default_passwd;
300         pw.pw_uid = 0;
301         pw.pw_gid = 0;
302         pw.pw_gecos = (char *)gecos;
303         pw.pw_dir = (char *)home;
304         pw.pw_shell = (char *)shell;
305
306         if (usegroup) {
307                 /* Add user to a group that already exists */
308                 struct group *g;
309
310                 g = getgrnam(usegroup);
311                 if (g == NULL)
312                         bb_error_msg_and_die("group %s does not exist", usegroup);
313
314                 pw.pw_gid = g->gr_gid;
315         }
316
317         /* grand finale */
318         return adduser(bb_path_passwd_file, &pw, makehome, setpass);
319 }