41dc9f0193202d3ce1e29e61aea5ed31e60007e0
[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  * 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 static const char default_shell[] = "/bin/sh";
57
58 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
59 /* shadow in use? */
60 static int shadow_enabled = 0;
61 #endif
62
63 /* remix */
64 /* EDR recoded such that the uid may be passed in *p */
65 static int passwd_study(const char *filename, struct passwd *p)
66 {
67         struct passwd *pw;
68         FILE *passwd;
69
70         const int min = 500;
71         const int max = 65000;
72
73         passwd = bb_wfopen(filename, "r");
74         if (!passwd)
75                 return 4;
76
77         /* EDR if uid is out of bounds, set to min */
78         if ((p->pw_uid > max) || (p->pw_uid < min))
79                 p->pw_uid = min;
80
81         /* stuff to do:  
82          * make sure login isn't taken;
83          * find free uid and gid;
84          */
85         while ((pw = fgetpwent(passwd))) {
86                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
87                         /* return 0; */
88                         return 1;
89                 }
90                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
91                         && (pw->pw_uid >= min)) {
92                         p->pw_uid = pw->pw_uid + 1;
93                 }
94         }
95
96         if (p->pw_gid == 0) {
97                 /* EDR check for an already existing gid */
98                 while (getgrgid(p->pw_uid) != NULL)
99                         p->pw_uid++;
100
101                 /* EDR also check for an existing group definition */
102                 if (getgrnam(p->pw_name) != NULL)
103                         return 3;
104
105                 /* EDR create new gid always = uid */
106                 p->pw_gid = p->pw_uid;
107         }
108
109         /* EDR bounds check */
110         if ((p->pw_uid > max) || (p->pw_uid < min))
111                 return 2;
112
113         /* return 1; */
114         return 0;
115 }
116
117 static void addgroup_wrapper(const char *login, gid_t gid)
118 {
119         char *cmd;
120
121         bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
122         system(cmd);
123         free(cmd);
124 }
125
126 static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
127
128 static void passwd_wrapper(const char *login)
129 {
130         static const char prog[] = "passwd";
131         execlp(prog, prog, login, NULL);
132         bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
133 }
134
135 /* putpwent(3) remix */
136 static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
137 {
138         FILE *passwd;
139         int r;
140 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
141         FILE *shadow;
142         struct spwd *sp;
143 #endif
144         int new_group = 1;
145
146         /* if using a pre-existing group, don't create one */
147         if (p->pw_gid != 0)
148                 new_group = 0;
149
150         /* make sure everything is kosher and setup uid && gid */
151         passwd = bb_wfopen(filename, "a");
152         if (passwd == NULL) {
153                 return 1;
154         }
155         fseek(passwd, 0, SEEK_END);
156
157         /* if (passwd_study(filename, p) == 0) { */
158         r = passwd_study(filename, p);
159         if (r) {
160                 if (r == 1)
161                         bb_error_msg("%s: login already in use", p->pw_name);
162                 else if (r == 2)
163                         bb_error_msg("illegal uid or no uids left");
164                 else if (r == 3)
165                         bb_error_msg("group name %s already in use", p->pw_name);
166                 else
167                         bb_error_msg("generic error.");
168                 return 1;
169         }
170
171         /* add to passwd */
172         if (putpwent(p, passwd) == -1) {
173                 return 1;
174         }
175         fclose(passwd);
176
177 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
178         /* add to shadow if necessary */
179         if (shadow_enabled) {
180                 shadow = bb_wfopen(bb_path_shadow_file, "a");
181                 if (shadow == NULL) {
182                         return 1;
183                 }
184                 fseek(shadow, 0, SEEK_END);
185                 sp = pwd_to_spwd(p);
186                 sp->sp_max = 99999;             /* debianish */
187                 sp->sp_warn = 7;
188                 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
189                                 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
190                                 sp->sp_warn);
191                 fclose(shadow);
192         }
193 #endif
194
195         if (new_group) {
196                 /* add to group */
197                 /* addgroup should be responsible for dealing w/ gshadow */
198                 addgroup_wrapper(p->pw_name, p->pw_gid);
199         }
200
201         /* Clear the umask for this process so it doesn't
202          * * screw up the permissions on the mkdir and chown. */
203         umask(0);
204
205         if (makehome) {
206                 /* mkdir */
207                 if (mkdir(p->pw_dir, 0755)) {
208                         bb_perror_msg("%s", p->pw_dir);
209                 }
210                 /* Set the owner and group so it is owned by the new user. */
211                 if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
212                         bb_perror_msg("%s", p->pw_dir);
213                 }
214                 /* Now fix up the permissions to 2755. Can't do it before now
215                  * since chown will clear the setgid bit */
216                 if (chmod(p->pw_dir, 02755)) {
217                         bb_perror_msg("%s", p->pw_dir);
218                 }
219         }
220
221         if (setpass) {
222                 /* interactively set passwd */
223                 passwd_wrapper(p->pw_name);
224         }
225
226         return 0;
227 }
228
229
230 /* return current uid (root is always uid == 0, right?) */
231 #ifndef CONFIG_ADDGROUP
232 static inline void if_i_am_not_root(void)
233 #else
234 void if_i_am_not_root(void)
235 #endif
236 {
237         if (geteuid()) {
238                 bb_error_msg_and_die( "Only root may add a user or group to the system.");
239         }
240 }
241
242 #define SETPASS                         1
243 #define MAKEHOME                        4
244
245 /*
246  * adduser will take a login_name as its first parameter.
247  *
248  * home
249  * shell
250  * gecos 
251  *
252  * can be customized via command-line parameters.
253  * ________________________________________________________________________ */
254 int adduser_main(int argc, char **argv)
255 {
256         struct passwd pw;
257         const char *login;
258         const char *gecos = default_gecos;
259         const char *home = NULL;
260         const char *shell = default_shell;
261         const char *usegroup = NULL;
262         int flags;
263         int setpass = 1;
264         int makehome = 1;
265
266         /* init */
267         if (argc < 2) {
268                 bb_show_usage();
269         }
270         /* get args */
271         flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
272
273         if (flags & SETPASS) {
274                 setpass = 0;
275         }
276         if (flags & MAKEHOME) {
277                 makehome = 0;
278         }
279
280         /* got root? */
281         if_i_am_not_root();
282
283         /* get login */
284         if (optind >= argc) {
285                 bb_error_msg_and_die( "no user specified");
286         }
287         login = argv[optind];
288
289         /* create string for $HOME if not specified already */
290         if (!home) {
291                 home = concat_path_file(default_home_prefix, login);
292         }
293 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
294         /* is /etc/shadow in use? */
295         shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
296 #endif
297
298         /* create a passwd struct */
299         pw.pw_name = (char *)login;
300         pw.pw_passwd = (char *)default_passwd;
301         pw.pw_uid = 0;
302         pw.pw_gid = 0;
303         pw.pw_gecos = (char *)gecos;
304         pw.pw_dir = (char *)home;
305         pw.pw_shell = (char *)shell;
306
307         if (usegroup) {
308                 /* Add user to a group that already exists */
309                 struct group *g;
310
311                 g = getgrnam(usegroup);
312                 if (g == NULL)
313                         bb_error_msg_and_die("group %s does not exist", usegroup);
314
315                 pw.pw_gid = g->gr_gid;
316         }
317
318         /* grand finale */
319         return adduser(bb_path_passwd_file, &pw, makehome, setpass);
320 }