66fcaa23f110d51b373c1805f2cd43bc541ce069
[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 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include "busybox.h"
36 #include "pwd.h"
37 #include "grp.h"
38
39 #define PASSWD_FILE     "/etc/passwd"
40 #define SHADOW_FILE             "/etc/shadow"
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
60 #include "shadow.h"
61
62 /* shadow in use? */
63 static int shadow_enabled = 0;
64 #endif
65
66 /* remix */
67 /* EDR recoded such that the uid may be passed in *p */
68 static int passwd_study(const char *filename, struct passwd *p)
69 {
70         struct passwd *pw;
71         FILE *passwd;
72
73         const int min = 500;
74         const int max = 65000;
75
76         passwd = wfopen(filename, "r");
77         if (!passwd)
78                 return 4;
79
80         /* EDR if uid is out of bounds, set to min */
81         if ((p->pw_uid > max) || (p->pw_uid < min))
82                 p->pw_uid = min;
83
84         /* stuff to do:  
85          * make sure login isn't taken;
86          * find free uid and gid;
87          */
88         while ((pw = fgetpwent(passwd))) {
89                 if (strcmp(pw->pw_name, p->pw_name) == 0) {
90                         /* return 0; */
91                         return 1;
92                 }
93                 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
94                         && (pw->pw_uid >= min)) {
95                         p->pw_uid = pw->pw_uid + 1;
96                 }
97         }
98
99         /* EDR check for an already existing gid */
100         while (getgrgid(p->pw_uid) != NULL)
101                 p->pw_uid++;
102
103         /* EDR also check for an existing group definition */
104         if (getgrnam(p->pw_name) != NULL)
105                 return 3;
106
107         /* EDR bounds check */
108         if ((p->pw_uid > max) || (p->pw_uid < min))
109                 return 2;
110
111         /* EDR create new gid always = uid */
112         p->pw_gid = p->pw_uid;
113
114         /* return 1; */
115         return 0;
116 }
117
118 static void addgroup_wrapper(const char *login, gid_t gid)
119 {
120         int argc = 3;
121         const char *argv0_save;
122         char group_id[8];
123         char group_name[32];
124         char *argv[] = { group_name, "-g", group_id };
125
126         argv0_save = applet_name;
127         applet_name = "addgroup";
128         safe_strncpy(group_name, login, 32);
129         sprintf(group_id, "%d", gid);
130         addgroup_main(argc, argv);
131         applet_name = argv0_save;
132 }
133
134 static void passwd_wrapper(const char *login)
135 {
136         static const char prog[] = "passwd";
137         execlp(prog, prog, login, NULL);
138         error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually", login);
139 }
140
141 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
142 /*
143  * pwd_to_spwd - create entries for new spwd structure
144  *
145  *      pwd_to_spwd() creates a new (struct spwd) containing the
146  *      information in the pointed-to (struct passwd).
147  */
148 #define DAY (24L*3600L)
149 #define WEEK (7*DAY)
150 #define SCALE DAY
151 static struct spwd *pwd_to_spwd(const struct passwd *pw)
152 {
153         static struct spwd sp;
154
155         /*
156          * Nice, easy parts first.  The name and passwd map directly
157          * from the old password structure to the new one.
158          */
159         sp.sp_namp = pw->pw_name;
160         sp.sp_pwdp = pw->pw_passwd;
161
162         /*
163          * Defaults used if there is no pw_age information.
164          */
165         sp.sp_min = 0;
166         sp.sp_max = (10000L * DAY) / SCALE;
167         sp.sp_lstchg = time((time_t *) 0) / SCALE;
168
169         /*
170          * These fields have no corresponding information in the password
171          * file.  They are set to uninitialized values.
172          */
173         sp.sp_warn = -1;
174         sp.sp_expire = -1;
175         sp.sp_inact = -1;
176         sp.sp_flag = -1;
177
178         return &sp;
179 }
180 #endif
181
182 /* putpwent(3) remix */
183 static int adduser(const char *filename, struct passwd *p)
184 {
185         FILE *passwd;
186         int r;
187 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
188         FILE *shadow;
189         struct spwd *sp;
190 #endif
191
192         /* make sure everything is kosher and setup uid && gid */
193         passwd = wfopen(filename, "a");
194         if (passwd == NULL) {
195                 /* return -1; */
196                 return 1;
197         }
198         fseek(passwd, 0, SEEK_END);
199
200         /* if (passwd_study(filename, p) == 0) { */
201         r = passwd_study(filename, p);
202         if (r) {
203                 if (r == 1)
204                         error_msg("%s: login already in use", p->pw_name);
205                 else if (r == 2)
206                         error_msg("illegal uid or no uids left");
207                 else if (r == 3)
208                         error_msg("group name %s already in use", p->pw_name);
209                 else
210                         error_msg("generic error.");
211                 /* return -1; */
212                 return 1;
213         }
214
215         /* add to passwd */
216         if (putpwent(p, passwd) == -1) {
217                 /* return -1; */
218                 return 1;
219         }
220         fclose(passwd);
221
222 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
223         /* add to shadow if necessary */
224         if (shadow_enabled) {
225                 shadow = wfopen(SHADOW_FILE, "a");
226                 if (shadow == NULL) {
227                         /* return -1; */
228                         return 1;
229                 }
230                 fseek(shadow, 0, SEEK_END);
231                 sp = pwd_to_spwd(p);
232                 sp->sp_max = 99999;             /* debianish */
233                 sp->sp_warn = 7;
234                 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
235                                 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
236                                 sp->sp_warn);
237                 fclose(shadow);
238         }
239 #endif
240
241         /* add to group */
242         /* addgroup should be responsible for dealing w/ gshadow */
243         addgroup_wrapper(p->pw_name, p->pw_gid);
244
245         /* Clear the umask for this process so it doesn't
246          * * screw up the permissions on the mkdir and chown. */
247         umask(0);
248
249         /* mkdir */
250         if (mkdir(p->pw_dir, 0755)) {
251                 perror_msg("%s", p->pw_dir);
252         }
253         /* Set the owner and group so it is owned by the new user. */
254         if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
255                 perror_msg("%s", p->pw_dir);
256         }
257         /* Now fix up the permissions to 2755. Can't do it before now
258          * since chown will clear the setgid bit */
259         if (chmod(p->pw_dir, 02755)) {
260                 perror_msg("%s", p->pw_dir);
261         }
262         /* interactively set passwd */
263         passwd_wrapper(p->pw_name);
264
265         return 0;
266 }
267
268
269 /* return current uid (root is always uid == 0, right?) */
270 static inline uid_t i_am_not_root(void)
271 {
272         return geteuid();
273 }
274
275 /*
276  * adduser will take a login_name as its first parameter.
277  *
278  * home
279  * shell
280  * gecos 
281  *
282  * can be customized via command-line parameters.
283  * ________________________________________________________________________ */
284 int adduser_main(int argc, char **argv)
285 {
286         int i = 0;
287         char opt;
288         const char *login;
289         const char *gecos;
290         const char *home = NULL;
291         const char *shell;
292
293         struct passwd pw;
294
295         /* init */
296         if (argc < 2) {
297                 show_usage();
298         }
299         gecos = default_gecos;
300         shell = default_shell;
301
302         /* get args */
303         while ((opt = getopt (argc, argv, "h:g:s:")) != -1)
304                 switch (opt) {
305                         case 'h':
306                                 home = argv[++i];
307                                 break;
308                         case 'g':
309                                 gecos = argv[++i];
310                                 break;
311                         case 's':
312                                 shell = argv[++i];
313                                 break;
314                         default:
315                                 show_usage ();
316                                 break;
317                 }
318
319         /* got root? */
320         if (i_am_not_root()) {
321                 error_msg_and_die( "Only root may add a user or group to the system.");
322         }
323
324         /* get login */
325         if (optind >= argc) {
326                 error_msg_and_die( "no user specified");
327         }
328         login = argv[optind];
329
330         /* create string for $HOME if not specified already */
331         if (!home) {
332                 home = concat_path_file(default_home_prefix, login);
333         }
334 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
335         /* is /etc/shadow in use? */
336         shadow_enabled = (0 == access(SHADOW_FILE, F_OK));
337 #endif
338
339         /* create a passwd struct */
340         pw.pw_name = (char *)login;
341         pw.pw_passwd = (char *)default_passwd;
342         pw.pw_uid = 0;
343         pw.pw_gid = 0;
344         pw.pw_gecos = (char *)gecos;
345         pw.pw_dir = (char *)home;
346         pw.pw_shell = (char *)shell;
347
348         /* grand finale */
349         return adduser(PASSWD_FILE, &pw);
350 }
351
352 /* $Id: adduser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */